// ── External ──
import { resolve } from 'path';
import { homedir } from 'os';
import { mkdirSync } from 'fs';

// ── Config & Types ──
import type { Config } from '../config/index.js';
import type { ToolRegistry, ToolExecutor, BrowserManager } from '../tools/index.js';
import type { AgentConfigRegistry, AgentRouter } from '../agents/index.js';
import type { SandboxManager } from '../sandbox/index.js';
import { setLogLevel } from '../logger.js';

// ── Daemon Modules ──
import { Lifecycle } from './lifecycle.js';
import { createModelRouter } from './models.js';
import { initMemory } from './memory.js';
import { initTools } from './tools.js';
import { initAgents } from './agents.js';
import { createMessageRouter } from './routing.js';
import { registerChannels } from './channels.js';
import { initSkills, initMcp, loadSystemPrompt, initPairingManager, createGateway, startServices } from './services.js';
import { CommandRegistry, registerBuiltinCommands } from '../commands/index.js';
import { ComponentRegistry } from '../intents/index.js';
import { RoutingPolicy } from '../routing/index.js';

// ── Infrastructure ──
import type { ModelRouter } from '../models/index.js';
import { SessionStore, SessionManager, parseDuration } from '../session/index.js';
import { HookEngine } from '../hooks/index.js';
import { createSessionTools, createAgentsListTool, createMessageSendTool, createCronTools, createGmailTools, createGcalTools, createGdocsTools, createGdriveTools, createGtasksTools } from '../tools/index.js';
import { ChannelRegistry } from '../channels/index.js';
import type { McpManager } from '../mcp/index.js';
import type { SkillRegistry, SkillInstaller } from '../skills/index.js';
import type { GatewayServer } from '../gateway/index.js';
import { AuditLogger, initAuditLogger } from '../audit/index.js';

export interface DaemonContext {
  config: Config;
  lifecycle: Lifecycle;
  sessionStore: SessionStore;
  sessionManager: SessionManager;
  hookEngine: HookEngine;
  modelRouter: ModelRouter;
  toolRegistry: ToolRegistry;
  toolExecutor: ToolExecutor;
  gateway: GatewayServer;
  channelRegistry: ChannelRegistry;
  mcpManager: McpManager;
  skillRegistry: SkillRegistry;
  skillInstaller: SkillInstaller;
  agentConfigRegistry: AgentConfigRegistry;
  agentRouter: AgentRouter;
  sandboxManager?: SandboxManager;
  browserManager?: BrowserManager;
}

export async function startDaemon(config: Config): Promise<DaemonContext> {
  // ── Log level ──
  setLogLevel(config.log_level);

  const lifecycle = new Lifecycle();

  // ── Data & Sessions ──
  const dataDir = process.env.FLYNN_DATA_DIR ?? resolve(homedir(), '.local/share/flynn');
  mkdirSync(dataDir, { recursive: true });

  // ── Audit Logger ──
  const auditLoggerInstance = new AuditLogger(config.audit);
  initAuditLogger(auditLoggerInstance);

  lifecycle.onShutdown(async () => {
    await auditLoggerInstance.close();
    console.log('Audit logger closed');
  });

  const sessionStore = new SessionStore(resolve(dataDir, 'sessions.db'));
  const sessionManager = new SessionManager(sessionStore, {
    enabled: config.history_index.enabled,
    maxKeywords: config.history_index.max_keywords,
    searchLimit: config.history_index.search_limit,
    minScore: config.history_index.min_score,
  });

  lifecycle.onShutdown(async () => {
    sessionStore.close();
    console.log('Session store closed');
  });

  const ttlMs = parseDuration(config.sessions?.ttl ?? '30d');
  if (ttlMs) {
    const pruneInterval = setInterval(() => {
      const cutoff = Math.floor((Date.now() - ttlMs) / 1000);
      const pruned = sessionStore.pruneStale(cutoff);
      if (pruned.length > 0) {
        sessionManager.evictSessions(pruned);
        console.log(`Pruned ${pruned.length} stale session(s) (TTL: ${config.sessions?.ttl ?? '30d'})`);
      }
    }, 3_600_000);
    lifecycle.onShutdown(async () => { clearInterval(pruneInterval); });
  }

  // ── Core Services ──
  const hookEngine = new HookEngine(config.hooks);
  const { toolRegistry, toolExecutor, browserManager } = initTools({ config, lifecycle, hookEngine });
  const { memoryStore, memoryDir } = await initMemory({ config, dataDir, lifecycle, toolRegistry });
  const mcpManager = await initMcp(config, lifecycle, toolRegistry);
  const { skillRegistry, skillInstaller } = initSkills(config, lifecycle);
  const { agentConfigRegistry, agentRouter, sandboxManager } = await initAgents({ config, lifecycle });

  // Ensure ToolExecutor can enforce sandbox execution at runtime.
  toolExecutor.setSandboxManager(sandboxManager);

  const modelRouter = createModelRouter(config);
  const commandRegistry = new CommandRegistry();
  registerBuiltinCommands(commandRegistry);
  const intentRegistry = new ComponentRegistry({
    matchThreshold: config.intents.match_threshold,
  });
  if (config.intents.enabled) {
    intentRegistry.loadRules(config.intents.rules);
  }
  const routingPolicy = new RoutingPolicy({
    enabled: config.routing_policy.enabled,
    fastPathThreshold: config.routing_policy.fast_path_threshold,
    llmThreshold: config.routing_policy.llm_threshold,
    defaultPath: config.routing_policy.default_path,
  });

  // Restore persisted model tier
  const { loadPreferences, savePreference } = await import('../preferences.js');
  const prefs = loadPreferences(dataDir);
  if (prefs.modelTier) {
    modelRouter.setTier(prefs.modelTier as import('../models/router.js').ModelTier);
  }
  modelRouter.setOnTierChange((tier) => savePreference(dataDir, 'modelTier', tier));

  const systemPrompt = loadSystemPrompt(config, skillRegistry);

  // ── Gateway & Channels ──
  const channelRegistry = new ChannelRegistry();
  const pairingStore = config.pairing.enabled ? sessionStore.getPairingStore() : undefined;
  const pairingManager = initPairingManager(config, pairingStore);

  let channelAgents: ReturnType<typeof createMessageRouter>['agents'] | null = null;

  const gateway = createGateway({
    config, sessionManager, modelRouter, systemPrompt, toolRegistry, toolExecutor,
    channelRegistry, pairingManager, lifecycle, memoryStore,
    getChannelAgents: () => channelAgents, commandRegistry, intentRegistry, routingPolicy,
  });

  const messageRouter = createMessageRouter({
    sessionManager, modelRouter, systemPrompt, toolRegistry, toolExecutor,
    config, memoryStore, agentConfigRegistry, agentRouter, sandboxManager, commandRegistry, intentRegistry, routingPolicy, skillRegistry,
  });
  channelRegistry.setMessageHandler(messageRouter.handler);
  channelAgents = messageRouter.agents;

  const { cronScheduler } = registerChannels({ config, channelRegistry, hookEngine, pairingManager, gateway });

  // ── Tier 1 Tools ──
  for (const tool of createSessionTools(sessionManager)) { toolRegistry.register(tool); }
  toolRegistry.register(createAgentsListTool(agentConfigRegistry));
  toolRegistry.register(createMessageSendTool(channelRegistry));
  if (cronScheduler) {
    for (const tool of createCronTools(cronScheduler)) { toolRegistry.register(tool); }
  }
  if (config.automation.gmail?.enabled) {
    for (const tool of createGmailTools(config.automation.gmail)) { toolRegistry.register(tool); }
  }
  if (config.automation.gcal?.enabled) {
    for (const tool of createGcalTools(config.automation.gcal)) { toolRegistry.register(tool); }
  }
  if (config.automation.gdocs?.enabled) {
    for (const tool of createGdocsTools(config.automation.gdocs)) { toolRegistry.register(tool); }
  }
  if (config.automation.gdrive?.enabled) {
    for (const tool of createGdriveTools(config.automation.gdrive)) { toolRegistry.register(tool); }
  }
  if (config.automation.gtasks?.enabled) {
    for (const tool of createGtasksTools(config.automation.gtasks)) { toolRegistry.register(tool); }
  }

  // ── Lifecycle ──
  await startServices({ config, lifecycle, channelRegistry, gateway, modelRouter, memoryDir, dataDir });

  return {
    config, lifecycle, sessionStore, sessionManager, hookEngine, modelRouter,
    toolRegistry, toolExecutor, gateway, channelRegistry, mcpManager,
    skillRegistry, skillInstaller, agentConfigRegistry, agentRouter,
    sandboxManager, browserManager,
  };
}

// ── Re-exports for backward compatibility ──
export { Lifecycle } from './lifecycle.js';
export { createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter } from './models.js';
