#!/usr/bin/env node
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'

import { RevolutionaryTypedFetch } from './core/typed-fetch.js'
import type { TypedFetchConfig } from './types/config.js'
import type { TypeSnapshotOptions } from './discovery/type-generator.js'

interface ParsedArgs {
  command: string
  base?: string
  out?: string
  namespace?: string
  banner?: string
  config?: string
  verbose?: boolean
}

const SHORT_FLAGS: Record<string, keyof ParsedArgs> = {
  b: 'base',
  o: 'out',
  n: 'namespace',
  c: 'config'
}

async function main(): Promise<void> {
  const parsed = parseArgs(process.argv.slice(2))
  const command = parsed.command || 'sync'

  switch (command) {
    case 'sync':
      await handleSync(parsed)
      break
    case 'help':
    case '--help':
      printHelp()
      break
    default:
      console.error(`Unknown command: ${command}`)
      printHelp()
      process.exitCode = 1
  }
}

function parseArgs(argv: string[]): ParsedArgs {
  const args: ParsedArgs = { command: '' }
  const positionals: string[] = []

  for (let i = 0; i < argv.length; i++) {
    const token = argv[i]!
    if (!token.startsWith('-')) {
      positionals.push(token)
      continue
    }

    if (token.startsWith('--')) {
      const segment = token.slice(2)
      const [rawFlag, maybeValue] = segment.split('=', 2)
      const flag = rawFlag ?? ''
      if (!flag) continue
      if (flag.startsWith('no-')) {
        ;(args as any)[flag.slice(3)] = false
        continue
      }

      if (maybeValue !== undefined) {
        ;(args as any)[flag] = maybeValue
        continue
      }

      const next = argv[i + 1]
      if (!next || next.startsWith('-')) {
        ;(args as any)[flag] = true
      } else {
        ;(args as any)[flag] = next
        i++
      }
      continue
    }

    const short = token.replace(/^-+/, '')
    const mapped = SHORT_FLAGS[short]
    if (mapped) {
      const next = argv[i + 1]
      if (!next || next.startsWith('-')) {
        throw new Error(`Flag -${short} requires a value`)
      }
      ;(args as any)[mapped] = next
      i++
    }
  }

  args.command = positionals[0] || args.command || 'sync'
  if (!args.base && positionals[1]) {
    args.base = positionals[1]
  }
  if (!args.out && positionals[2]) {
    args.out = positionals[2]
  }

  return args
}

async function handleSync(args: ParsedArgs): Promise<void> {
  const config = await loadConfig(args.config)
  const client = new RevolutionaryTypedFetch(config)

  const baseURL = args.base || config?.request?.baseURL
  if (!baseURL) {
    throw new Error('A base URL is required. Pass --base or set request.baseURL in your config file.')
  }

  if (args.verbose) {
    console.log(`🔍 Discovering schema from ${baseURL}`)
  }

  await client.discover(baseURL)

  const snapshotOptions: TypeSnapshotOptions = {}
  if (args.out) snapshotOptions.outFile = args.out
  if (args.namespace) snapshotOptions.namespace = args.namespace
  if (args.banner) snapshotOptions.banner = args.banner

  const code = await client.exportTypes(snapshotOptions)
  if (args.out) {
    if (args.verbose) {
      console.log(`📄 Type snapshot written to ${resolve(args.out)}`)
    } else {
      console.log(`📄 Wrote ${args.out}`)
    }
  } else {
    process.stdout.write(code)
  }
}

async function loadConfig(path?: string): Promise<TypedFetchConfig | undefined> {
  if (!path) return undefined
  const resolved = resolve(path)
  const raw = await readFile(resolved, 'utf8')
  return JSON.parse(raw)
}

function printHelp(): void {
  console.log(`typedfetch <command> [options]

Commands:
  sync [--base <url>] [--out <file>]   Discover the API and emit TypeScript definitions
  help                                  Show this help message

Options:
  --base, -b       Base URL to discover (falls back to config request.baseURL)
  --out, -o        File path for the generated declaration file (prints to stdout if omitted)
  --namespace, -n  Wrap declarations in a namespace
  --banner         Add a custom banner comment to the snapshot
  --config, -c     Path to a JSON config file matching TypedFetchConfig
  --verbose        Print progress information
`)
}

main().catch((error) => {
  console.error(error instanceof Error ? error.message : error)
  process.exitCode = 1
})
