import type { App } from '@types'
import { generateTemplates, type TemplateType } from '%templates'
import color from 'kleur'
import { existsSync, mkdirSync, writeFileSync } from 'fs'
import { basename, join } from 'path'
import { del, get, getManifest, HOST, post } from '../http'
import { confirm, prompt } from '../prompts'
import { resolveAppName } from '../name'
import { pushApp } from './sync'

export const STATE_ICONS: Record<string, string> = {
  running: color.green('●'),
  starting: color.yellow('◎'),
  stopped: color.gray('◯'),
  invalid: color.red('◌'),
}

export async function configShow() {
  console.log(`Host: ${color.bold(HOST)}`)

  const source = process.env.TOES_URL
    ? 'TOES_URL'
    : process.env.TOES_HOST
      ? 'TOES_HOST' + (process.env.PORT ? ' + PORT' : '')
      : process.env.NODE_ENV === 'production'
        ? 'default (production)'
        : 'default (development)'

  console.log(`Source: ${color.gray(source)}`)

  if (process.env.TOES_URL) {
    console.log(`  TOES_URL=${process.env.TOES_URL}`)
  }
  if (process.env.TOES_HOST) {
    console.log(`  TOES_HOST=${process.env.TOES_HOST}`)
  }
  if (process.env.PORT) {
    console.log(`  PORT=${process.env.PORT}`)
  }
  if (process.env.NODE_ENV) {
    console.log(`  NODE_ENV=${process.env.NODE_ENV}`)
  }
}

export async function infoApp(arg?: string) {
  const name = resolveAppName(arg)
  if (!name) return

  const app: App | undefined = await get(`/api/apps/${name}`)
  if (!app) {
    console.error(`App not found: ${name}`)
    return
  }

  const icon = STATE_ICONS[app.state] ?? '◯'
  console.log(`${icon} ${color.bold(app.name)} ${app.tool ? '[tool]' : ''}`)
  console.log(`  State:   ${app.state}`)
  if (app.port) {
    console.log(`  Port:    ${app.port}`)
    console.log(`  URL:     http://localhost:${app.port}`)
  }
  if (app.started) {
    const uptime = Date.now() - app.started
    const seconds = Math.floor(uptime / 1000) % 60
    const minutes = Math.floor(uptime / 60000) % 60
    const hours = Math.floor(uptime / 3600000)
    const parts = []
    if (hours) parts.push(`${hours}h`)
    if (minutes) parts.push(`${minutes}m`)
    parts.push(`${seconds}s`)
    console.log(`  Uptime:  ${parts.join(' ')}`)
  }
  if (app.error) console.log(`  Error:   ${color.red(app.error)}`)
}

interface ListAppsOptions {
  tools?: boolean
  all?: boolean
}

export async function listApps(options: ListAppsOptions) {
  const allApps: App[] | undefined = await get('/api/apps')
  if (!allApps) return

  if (options.all) {
    const apps = allApps.filter((app) => !app.tool)
    const tools = allApps.filter((app) => app.tool)

    if (tools.length === 0) {
      // No tools, just list apps without header/indent
      for (const app of apps) {
        console.log(`${STATE_ICONS[app.state] ?? '◯'} ${app.name}`)
      }
    } else {
      if (apps.length > 0) {
        console.log('apps:')
        for (const app of apps) {
          console.log(`  ${STATE_ICONS[app.state] ?? '◯'} ${app.name}`)
        }
        console.log()
      }
      console.log('tools:')
      for (const tool of tools) {
        console.log(`  ${STATE_ICONS[tool.state] ?? '◯'} ${tool.name}`)
      }
    }
  } else {
    const filtered = allApps.filter((app) => {
      if (options.tools) return app.tool
      return !app.tool
    })

    for (const app of filtered) {
      console.log(`${STATE_ICONS[app.state] ?? '◯'} ${app.name}`)
    }
  }
}

interface NewAppOptions {
  ssr?: boolean
  bare?: boolean
  spa?: boolean
}

export async function newApp(name: string | undefined, options: NewAppOptions) {
  const appPath = name ? join(process.cwd(), name) : process.cwd()
  const appName = name ?? basename(process.cwd())

  // Determine template type from flags
  let template: TemplateType = 'ssr'
  if (options.bare) template = 'bare'
  else if (options.spa) template = 'spa'

  if (name && existsSync(appPath)) {
    console.error(`Directory already exists: ${name}`)
    return
  }

  const filesToCheck = ['index.tsx', 'package.json', 'tsconfig.json']
  const existing = filesToCheck.filter((f) => existsSync(join(appPath, f)))
  if (existing.length > 0) {
    console.error(`Files already exist: ${existing.join(', ')}`)
    return
  }

  const templateLabel = template === 'ssr' ? '' : ` (${template})`
  const ok = await confirm(`Create ${color.bold(appName)}${templateLabel} in ${appPath}?`)
  if (!ok) return

  const templates = generateTemplates(appName, template)

  // Create directories for all template files
  for (const filename of Object.keys(templates)) {
    const dir = join(appPath, filename, '..')
    mkdirSync(dir, { recursive: true })
  }

  for (const [filename, content] of Object.entries(templates)) {
    writeFileSync(join(appPath, filename), content)
  }

  process.chdir(appPath)
  await pushApp()

  console.log(color.green(`✓ Created ${appName}`))
  console.log()
  console.log('Next steps:')
  if (name) {
    console.log(`  cd ${name}`)
  }
  console.log('  bun install')
  console.log('  bun dev')
}

export async function openApp(arg?: string) {
  const name = resolveAppName(arg)
  if (!name) return

  const app: App | undefined = await get(`/api/apps/${name}`)
  if (!app) {
    console.error(`App not found: ${name}`)
    return
  }
  if (app.state !== 'running') {
    console.error(`App is not running: ${name}`)
    return
  }
  const url = `http://localhost:${app.port}`
  console.log(`Opening ${url}`)
  Bun.spawn(['open', url])
}

export async function renameApp(arg: string | undefined, newName: string) {
  const name = resolveAppName(arg)
  if (!name) return

  const result = await getManifest(name)
  if (result === null) return
  if (!result.exists) {
    console.error(`App not found on server: ${name}`)
    return
  }

  const expected = `sudo rename ${name} ${newName}`
  console.log(`This will rename ${color.bold(name)} to ${color.bold(newName)}.`)
  const answer = await prompt(`Type "${expected}" to confirm: `)

  if (answer !== expected) {
    console.log('Aborted.')
    return
  }

  const response = await post<{ ok: boolean, error?: string, name?: string }>(`/api/apps/${name}/rename`, { name: newName })
  if (!response) return

  if (!response.ok) {
    console.error(color.red(`Error: ${response.error}`))
    return
  }

  console.log(color.green(`✓ Renamed ${name} to ${response.name}`))
}

export async function restartApp(arg?: string) {
  const name = resolveAppName(arg)
  if (!name) return
  await post(`/api/apps/${name}/restart`)
}

export async function rmApp(arg?: string) {
  const name = resolveAppName(arg)
  if (!name) return

  const result = await getManifest(name)
  if (result === null) return
  if (!result.exists) {
    console.error(`App not found on server: ${name}`)
    return
  }

  const expected = `sudo rm ${name}`
  console.log(`This will ${color.red('permanently delete')} ${color.bold(name)} from the server.`)
  const answer = await prompt(`Type "${expected}" to confirm: `)

  if (answer !== expected) {
    console.log('Aborted.')
    return
  }

  const success = await del(`/api/sync/apps/${name}`)
  if (success) {
    console.log(color.green(`✓ Removed ${name}`))
  }
}

export async function startApp(arg?: string) {
  const name = resolveAppName(arg)
  if (!name) return
  await post(`/api/apps/${name}/start`)
}

export async function stopApp(arg?: string) {
  const name = resolveAppName(arg)
  if (!name) return
  await post(`/api/apps/${name}/stop`)
}
