import path from "path"
import { pathToFileURL } from "url"
import { existsSync } from "fs"
import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser"
import * as Log from "@saeeol/core/util/log"
import { Global } from "@saeeol/core/global"
import { ModesMigrator } from "../modes-migrator"
import { fetchOrganizationModes } from "@saeeol/gateway"
import { RulesMigrator } from "../rules-migrator"
import { WorkflowsMigrator } from "../workflows-migrator"
import { McpMigrator } from "../mcp-migrator"
import { IgnoreMigrator } from "../ignore-migrator"
import type { Config } from "../../config/config"
import type { ConfigAgent } from "../../config/agent"

const log = Log.create({ service: "saeeol.config" })

type MergeFn = (target: Config.Info, source: Config.Info) => Config.Info

export async function loadLegacyConfigs(input: {
  projectDir: string
  merge: MergeFn
}): Promise<{ config: Config.Info; warnings: Config.Warning[] }> {
  const warnings: Config.Warning[] = []
  let result: Config.Info = {}

  try {
    const migration = await ModesMigrator.migrate({ projectDir: input.projectDir })
    if (Object.keys(migration.agents).length > 0) {
      result = input.merge(result, { agent: migration.agents })
      log.debug("loaded saeeol custom modes", {
        count: Object.keys(migration.agents).length,
        modes: Object.keys(migration.agents),
      })
    }
    for (const skipped of migration.skipped) {
      log.debug("skipped saeeol mode", { slug: skipped.slug, reason: skipped.reason })
    }
  } catch (err) {
    log.warn("failed to load saeeol modes", { error: err })
  }

  try {
    const migration = await WorkflowsMigrator.migrate({ projectDir: input.projectDir })
    if (Object.keys(migration.commands).length > 0) {
      result = input.merge(result, { command: migration.commands })
      log.debug("loaded saeeol workflows as commands", {
        count: Object.keys(migration.commands).length,
        commands: Object.keys(migration.commands),
      })
    }
  } catch (err) {
    log.warn("failed to load saeeol workflows", { error: err })
  }

  try {
    const migration = await RulesMigrator.migrate({ projectDir: input.projectDir })
    if (migration.instructions.length > 0) {
      result = input.merge(result, { instructions: migration.instructions })
      log.debug("loaded saeeol rules", {
        count: migration.instructions.length,
        files: migration.instructions,
      })
    }
    for (const warning of migration.warnings) {
      log.debug("saeeol rules warning", { warning })
    }
  } catch (err) {
    log.warn("failed to load saeeol rules", { error: err })
  }

  const skipGlobal = process.env["SAEEOL_PLATFORM"] !== "vscode"
  const mcp = await McpMigrator.loadMcpConfig(input.projectDir, skipGlobal)
  if (Object.keys(mcp).length > 0) {
    result = input.merge(result, { mcp })
  }

  try {
    const permission = await IgnoreMigrator.loadIgnoreConfig(input.projectDir)
    if (Object.keys(permission).length > 0) {
      result = input.merge(result, { permission })
      log.debug("loaded saeeol ignore patterns", {
        hasRead: !!(permission as Record<string, unknown>).read,
        hasEdit: !!(permission as Record<string, unknown>).edit,
      })
    }
  } catch (err) {
    log.warn("failed to load saeeol ignore patterns", { error: err })
  }

  return { config: result, warnings }
}

export async function loadOrganizationModes(
  auth: Record<string, any>,
): Promise<{ agents: Record<string, ConfigAgent.Info>; warnings: Config.Warning[] }> {
  const warnings: Config.Warning[] = []
  try {
    const saeeol = auth["saeeol"]
    if (saeeol?.type === "oauth" && saeeol.access && saeeol.accountId) {
      const modes = await fetchOrganizationModes(saeeol.access, saeeol.accountId)
      if (modes.length > 0) {
        const agents = ModesMigrator.convertOrganizationModes(modes)
        log.debug("loaded organization custom modes", {
          count: modes.length,
          modes: modes.map((m: any) => m.slug),
        })
        return { agents, warnings }
      }
    }
  } catch (err) {
    log.warn("failed to load organization custom modes", { error: err })
  }
  return { agents: {}, warnings }
}

const GLOBAL_CONFIG_FILES = ["config.json", "saeeol.json", "saeeol.jsonc"]

export async function migrateBashPermission() {
  const files = GLOBAL_CONFIG_FILES.map((f) => path.join(Global.Path.config, f))
  const legacy = path.join(Global.Path.config, "config")
  const existing = files.filter((f) => existsSync(f))
  const hasLegacy = existsSync(legacy)

  if (existing.length === 0 && !hasLegacy) return

  for (const file of existing) {
    const text = await Bun.file(file)
      .text()
      .catch(() => "")
    const data = parseJsonc(text) ?? {}
    if (data.permission?.bash) return
  }

  if (hasLegacy) {
    const toml = await import(pathToFileURL(legacy).href, { with: { type: "toml" } }).catch(() => undefined)
    if (toml?.default?.permission?.bash) return
  }

  const target = existing.length > 0 ? existing[existing.length - 1] : path.join(Global.Path.config, "config.json")
  const text = await Bun.file(target)
    .text()
    .catch(() => "{}")

  if (target.endsWith(".jsonc")) {
    const edits = modify(text, ["permission", "bash"], "allow", {
      formattingOptions: { insertSpaces: true, tabSize: 2 },
    })
    await Bun.write(target, applyEdits(text, edits))
    log.info("migrated bash permission to allow for existing user", { path: target })
    return
  }

  const data = parseJsonc(text) ?? {}
  const merged = { ...data, permission: { ...data.permission, bash: "allow" } }
  await Bun.write(target, JSON.stringify(merged, null, 2))
  log.info("migrated bash permission to allow for existing user", { path: target })
}
