import { AgentRuntime, type AgentRuntimeConfig, createLogger, LOG_CATEGORY } from '@vibeeditor/agent';
import * as fs from 'fs/promises';
import * as path from 'path';
import type { LLMGateway } from '@vibeeditor/agent';
import type { SessionMessage } from '@vibeeditor/agent';

const log = createLogger(LOG_CATEGORY.WORKSPACE);

const VIBEEDITOR_DIR = '.vibeeditor';
const WORKSPACE_FILE = 'workspace.json';

export interface StoredTab {
  path: string;
  cursorLine?: number;
  cursorColumn?: number;
}

/** 持久化的 Agent 会话数据 */
export interface WorkspaceAgentSession {
  id: string;
  name: string;
  nameAutoGenerated: boolean;
  createdAt: number;
  lastMessageAt?: number;
  messages: { id: string; role: string; content: string; timestamp: number; agentId?: string }[];
}

/** 工作区级别的 LLM 配置（预留接口，暂不实现功能） */
export interface WorkspaceLLMConfig {
  providerId?: string;
  model?: string;
  temperature?: number;
  maxTokens?: number;
  systemPrompt?: string;
}

/** 工作区级别的 MCP 配置（预留接口，暂不实现功能） */
export interface WorkspaceMcpConfig {
  servers?: Record<string, unknown>[];
}

export interface WorkspaceData {
  workspaceId: string;
  rootPath: string;
  rootName: string;
  lightweight: boolean;
  openTabs: StoredTab[];
  activeTabPath?: string;
  createdAt: number;
  lastOpenedAt: number;
  agentSessions?: WorkspaceAgentSession[];
  llm?: WorkspaceLLMConfig;
  mcp?: WorkspaceMcpConfig;
}

interface ActiveWorkspace {
  runtime: AgentRuntime;
  data: WorkspaceData;
  config: AgentRuntimeConfig;
  lightweight: boolean;
}

export class WorkspaceManager {
  private workspaces = new Map<string, ActiveWorkspace>();
  private configDir: string;

  constructor(configDir: string) {
    this.configDir = configDir;
  }

  async openWorkspace(rootPath: string, llmGateway: LLMGateway, lightweight = false): Promise<WorkspaceData> {
    const startMs = Date.now();
    const absRoot = path.resolve(rootPath);

    if (!lightweight) {
      let stat: { isDirectory(): boolean };
      try {
        stat = await fs.stat(absRoot);
      } catch {
        throw new Error(`Path does not exist: ${absRoot}`);
      }
      if (!stat.isDirectory()) {
        throw new Error(`Path is not a directory: ${absRoot}`);
      }
    }

    const activeProvider = llmGateway.getActiveProvider();
    const config: AgentRuntimeConfig = {
      mode: 'build',
      provider: {
        apiUrl: activeProvider?.apiUrl || '',
        apiKey: activeProvider?.apiKey || '',
        model: activeProvider?.model || '',
      },
      workspaceRoot: absRoot,
      mcpServers: undefined,
    };

    const runtime = new AgentRuntime(config);

    const existingData = lightweight ? null : await this.readWorkspaceFile(absRoot);
    const workspaceId = existingData?.workspaceId || `ws_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const now = Date.now();

    const data: WorkspaceData = {
      workspaceId,
      rootPath: absRoot,
      rootName: path.basename(absRoot),
      lightweight,
      openTabs: existingData?.openTabs || [],
      activeTabPath: existingData?.activeTabPath,
      agentSessions: existingData?.agentSessions,
      llm: existingData?.llm,
      mcp: existingData?.mcp,
      createdAt: existingData?.createdAt || now,
      lastOpenedAt: now,
    };

    this.workspaces.set(workspaceId, { runtime, data, config, lightweight });

    if (data.agentSessions && data.agentSessions.length > 0) {
      for (const s of data.agentSessions) {
        const msgs: SessionMessage[] = s.messages.map(m => ({
          id: m.id,
          role: m.role as 'user' | 'agent' | 'system',
          content: m.content,
          timestamp: m.timestamp,
          agentId: m.agentId,
        }));
        runtime.restoreSession(s.id, msgs);
      }
    }

    if (!lightweight) {
      await this.writeWorkspaceFile(absRoot, data);
    }

    log.info(`Workspace opened: id=${workspaceId}, root="${absRoot}", lightweight=${lightweight}, ${Date.now() - startMs}ms`, { workspaceId, rootPath: absRoot });
    return data;
  }

  getRuntime(workspaceId: string): AgentRuntime | undefined {
    return this.workspaces.get(workspaceId)?.runtime;
  }

  /** 替换 workspace 的 runtime（用于从 agent 路由缓存新创建的 runtime） */
  cacheRuntime(workspaceId: string, runtime: AgentRuntime): void {
    const ws = this.workspaces.get(workspaceId);
    if (ws) {
      try { ws.runtime.dispose(); } catch { /* ignore */ }
      ws.runtime = runtime;
    }
  }

  getWorkspaceData(workspaceId: string): WorkspaceData | undefined {
    return this.workspaces.get(workspaceId)?.data;
  }

  async updateWorkspaceData(
    workspaceId: string,
    update: Partial<Pick<WorkspaceData, 'openTabs' | 'activeTabPath'>>
  ): Promise<void> {
    const ws = this.workspaces.get(workspaceId);
    if (!ws) throw new Error(`Workspace not found: ${workspaceId}`);

    if (update.openTabs) ws.data.openTabs = update.openTabs;
    if (update.activeTabPath !== undefined) ws.data.activeTabPath = update.activeTabPath;
    ws.data.lastOpenedAt = Date.now();

    if (!ws.lightweight) {
      await this.writeWorkspaceFile(ws.data.rootPath, ws.data);
    }
  }

  /** 保存或更新单个 Agent 会话到工作区数据并立即写盘 */
  async saveAgentSession(
    workspaceId: string,
    sessionData: WorkspaceAgentSession
  ): Promise<void> {
    const ws = this.workspaces.get(workspaceId);
    if (!ws) throw new Error(`Workspace not found: ${workspaceId}`);

    if (!ws.data.agentSessions) ws.data.agentSessions = [];
    const idx = ws.data.agentSessions.findIndex(s => s.id === sessionData.id);
    if (idx !== -1) {
      ws.data.agentSessions[idx] = sessionData;
    } else {
      ws.data.agentSessions.push(sessionData);
    }
    ws.data.lastOpenedAt = Date.now();

    if (!ws.lightweight) {
      await this.writeWorkspaceFile(ws.data.rootPath, ws.data);
    }
    log.debug(`Agent session saved: workspaceId=${workspaceId}, sessionId=${sessionData.id}, messages=${sessionData.messages.length}, lightweight=${ws.lightweight}`);
  }

  /** 删除工作区下的单个 Agent 会话 */
  async deleteAgentSession(workspaceId: string, sessionId: string): Promise<void> {
    const ws = this.workspaces.get(workspaceId);
    if (!ws) throw new Error(`Workspace not found: ${workspaceId}`);

    if (ws.data.agentSessions) {
      ws.data.agentSessions = ws.data.agentSessions.filter(s => s.id !== sessionId);
    }
    ws.runtime.deleteSession(sessionId);
    ws.data.lastOpenedAt = Date.now();

    if (!ws.lightweight) {
      await this.writeWorkspaceFile(ws.data.rootPath, ws.data);
    }
  }

  /** 获取工作区下所有 Agent 会话 */
  getAgentSessions(workspaceId: string): WorkspaceAgentSession[] {
    const ws = this.workspaces.get(workspaceId);
    if (!ws) return [];
    return ws.data.agentSessions || [];
  }

  async closeWorkspace(workspaceId: string): Promise<void> {
    const ws = this.workspaces.get(workspaceId);
    if (!ws) return;

    const rootPath = ws.data.rootPath;
    if (!ws.lightweight) {
      try { await this.writeWorkspaceFile(ws.data.rootPath, ws.data); } catch { /* ignore */ }
    }
    try { await ws.runtime.dispose(); } catch { /* ignore */ }
    this.workspaces.delete(workspaceId);
    log.info(`Workspace closed: id=${workspaceId}, root="${rootPath}", lightweight=${ws.lightweight}`, { workspaceId, rootPath });
  }

  async disposeAll(): Promise<void> {
    for (const id of this.workspaces.keys()) {
      await this.closeWorkspace(id);
    }
  }

  /** 读取工作区根目录下的 .vibeeditor/workspace.json */
  private async readWorkspaceFile(rootPath: string): Promise<WorkspaceData | null> {
    const filePath = path.join(rootPath, VIBEEDITOR_DIR, WORKSPACE_FILE);
    try {
      const raw = await fs.readFile(filePath, 'utf-8');
      const data = JSON.parse(raw) as WorkspaceData;
      log.debug(`Workspace file read: ${filePath} (${raw.length} chars)`);
      return data;
    } catch {
      return null;
    }
  }

  /** 写入工作区配置到 .vibeeditor/workspace.json */
  private async writeWorkspaceFile(rootPath: string, data: WorkspaceData): Promise<void> {
    const dir = path.join(rootPath, VIBEEDITOR_DIR);
    const filePath = path.join(dir, WORKSPACE_FILE);
    try {
      const content = JSON.stringify(data, null, 2);
      await fs.mkdir(dir, { recursive: true });
      await fs.writeFile(filePath, content, 'utf-8');
      log.debug(`Workspace file written: ${filePath} (${content.length} chars)`);
    } catch (e: any) {
      log.warn(`Workspace file write failed: ${e.message}`, { filePath });
    }
  }
}
