import * as fs from 'fs/promises';
import * as path from 'path';
import { Lock } from '../../utils/lock.js';
import { z } from 'zod';
import { getDataDir } from '../../constants/paths.js';

/**
 * 存储错误类型
 */
export class StorageNotFoundError extends Error {
  constructor(
    public readonly key: string[],
    message?: string
  ) {
    super(message || `Resource not found: ${key.join('/')}`);
    this.name = 'StorageNotFoundError';
  }
}

/**
 * 获取默认存储目录
 * 统一存储在 ~/.ai-terminal-assistant/data/
 */
export function getDefaultStorageDir(): string {
  return getDataDir();
}

/**
 * 存储状态
 */
interface StorageState {
  dir: string;
  initialized: boolean;
}

let storageState: StorageState | null = null;

/**
 * 初始化存储
 */
export async function initStorage(storageDir?: string): Promise<string> {
  if (storageState?.initialized) {
    return storageState.dir;
  }

  const dir = storageDir || getDefaultStorageDir();

  // 创建必要的目录
  await fs.mkdir(path.join(dir, 'project'), { recursive: true });
  await fs.mkdir(path.join(dir, 'session'), { recursive: true });
  await fs.mkdir(path.join(dir, 'message'), { recursive: true });
  await fs.mkdir(path.join(dir, 'part'), { recursive: true });
  await fs.mkdir(path.join(dir, 'todo'), { recursive: true });

  storageState = { dir, initialized: true };
  return dir;
}

/**
 * 获取存储目录
 */
export async function getStorageDir(): Promise<string> {
  if (!storageState?.initialized) {
    return initStorage();
  }
  return storageState.dir;
}

/**
 * 重置存储状态（用于测试）
 */
export function resetStorageState(): void {
  storageState = null;
}

/**
 * 读取 JSON 文件
 * @param key 路径键数组，如 ['session', 'projectId', 'sessionId']
 */
export async function read<T>(key: string[], schema?: z.ZodType<T>): Promise<T> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...key) + '.json';

  using _ = await Lock.read(target);

  try {
    const content = await fs.readFile(target, 'utf-8');
    const data = JSON.parse(content);

    if (schema) {
      return schema.parse(data);
    }
    return data as T;
  } catch (e) {
    if (e instanceof Error && 'code' in e && (e as NodeJS.ErrnoException).code === 'ENOENT') {
      throw new StorageNotFoundError(key);
    }
    throw e;
  }
}

/**
 * 写入 JSON 文件
 * @param key 路径键数组
 * @param content 要写入的内容
 */
export async function write<T>(key: string[], content: T): Promise<void> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...key) + '.json';

  // 确保父目录存在
  await fs.mkdir(path.dirname(target), { recursive: true });

  using _ = await Lock.write(target);
  await fs.writeFile(target, JSON.stringify(content, null, 2), 'utf-8');
}

/**
 * 更新 JSON 文件
 * @param key 路径键数组
 * @param fn 更新函数
 */
export async function update<T>(key: string[], fn: (draft: T) => void): Promise<T> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...key) + '.json';

  using _ = await Lock.write(target);

  try {
    const content = await fs.readFile(target, 'utf-8');
    const data = JSON.parse(content) as T;
    fn(data);
    await fs.writeFile(target, JSON.stringify(data, null, 2), 'utf-8');
    return data;
  } catch (e) {
    if (e instanceof Error && 'code' in e && (e as NodeJS.ErrnoException).code === 'ENOENT') {
      throw new StorageNotFoundError(key);
    }
    throw e;
  }
}

/**
 * 删除 JSON 文件
 * @param key 路径键数组
 */
export async function remove(key: string[]): Promise<void> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...key) + '.json';

  try {
    await fs.unlink(target);
  } catch {
    // 忽略不存在的文件
  }
}

/**
 * 删除目录
 * @param key 路径键数组
 */
export async function removeDir(key: string[]): Promise<void> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...key);

  try {
    await fs.rm(target, { recursive: true, force: true });
  } catch {
    // 忽略错误
  }
}

/**
 * 列出目录下的所有 key
 * @param prefix 路径前缀
 * @returns 完整的 key 路径数组
 */
export async function list(prefix: string[]): Promise<string[][]> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...prefix);

  try {
    const entries = await fs.readdir(target, { withFileTypes: true });
    const results: string[][] = [];

    for (const entry of entries) {
      if (entry.isFile() && entry.name.endsWith('.json')) {
        // 移除 .json 扩展名
        const name = entry.name.slice(0, -5);
        results.push([...prefix, name]);
      } else if (entry.isDirectory()) {
        // 递归列出子目录
        const subResults = await list([...prefix, entry.name]);
        results.push(...subResults);
      }
    }

    return results.sort();
  } catch {
    return [];
  }
}

/**
 * 列出目录下的直接子项（不递归）
 * @param prefix 路径前缀
 * @returns 子项名称数组
 */
export async function listDirect(prefix: string[]): Promise<string[]> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...prefix);

  try {
    const entries = await fs.readdir(target, { withFileTypes: true });
    const results: string[] = [];

    for (const entry of entries) {
      if (entry.isFile() && entry.name.endsWith('.json')) {
        results.push(entry.name.slice(0, -5));
      } else if (entry.isDirectory()) {
        results.push(entry.name);
      }
    }

    return results.sort();
  } catch {
    return [];
  }
}

/**
 * 检查文件是否存在
 */
export async function exists(key: string[]): Promise<boolean> {
  const dir = await getStorageDir();
  const target = path.join(dir, ...key) + '.json';

  try {
    await fs.access(target);
    return true;
  } catch {
    return false;
  }
}
