import { Hono } from 'hono';
import { decryptSecret, encryptSecret } from '../crypto/aes';
import { computeTotpCode, normalizeBase32Secret } from '../crypto/totp';
import { newShareTokenForInsert } from '../lib/ensureShareToken';

type ExportItem = {
  label: string;
  issuer: string | null;
  secret: string;
  algorithm: string;
  digits: number;
  period: number;
};

type AccountRow = {
  id: string;
  label: string;
  issuer: string | null;
  encrypted_secret: string;
  algorithm: string;
  digits: number;
  period: number;
};

function parseImportItem(raw: unknown): ExportItem | null {
  if (!raw || typeof raw !== 'object') return null;
  const o = raw as Record<string, unknown>;
  const label = typeof o.label === 'string' ? o.label.trim() : '';
  const issuer =
    typeof o.issuer === 'string' && o.issuer.trim()
      ? o.issuer.trim()
      : null;
  const secretRaw = typeof o.secret === 'string' ? o.secret.trim() : '';
  if (!label || !secretRaw) return null;
  const algorithm =
    typeof o.algorithm === 'string' && o.algorithm.trim()
      ? o.algorithm.trim()
      : 'SHA1';
  const digits =
    typeof o.digits === 'number' && Number.isFinite(o.digits)
      ? Math.min(10, Math.max(6, Math.floor(o.digits)))
      : 6;
  const period =
    typeof o.period === 'number' && Number.isFinite(o.period)
      ? Math.min(120, Math.max(10, Math.floor(o.period)))
      : 30;
  return { label, issuer, secret: secretRaw, algorithm, digits, period };
}

export function registerImportExportRoutes(
  authed: Hono<{ Bindings: Env }>,
): void {
  authed.get('/export', async (c) => {
    const { results } = await c.env.DB.prepare(
      `SELECT id, label, issuer, encrypted_secret, algorithm, digits, period FROM totp_accounts ORDER BY sort_order ASC, created_at ASC`,
    ).all<AccountRow>();

    const items: ExportItem[] = [];
    for (const row of results ?? []) {
      try {
        const secret = await decryptSecret(
          c.env.MASTER_KEY,
          row.encrypted_secret,
        );
        items.push({
          label: row.label,
          issuer: row.issuer,
          secret: normalizeBase32Secret(secret),
          algorithm: row.algorithm,
          digits: row.digits,
          period: row.period,
        });
      } catch {
        continue;
      }
    }
    return c.json({
      version: 1,
      exportedAt: new Date().toISOString(),
      items,
    });
  });

  authed.post('/import', async (c) => {
    let body: { items?: unknown };
    try {
      body = await c.req.json();
    } catch {
      return c.json({ error: '无效的 JSON' }, 400);
    }
    if (!Array.isArray(body.items)) {
      return c.json({ error: '需要 items 数组' }, 400);
    }

    const maxRow = await c.env.DB.prepare(
      'SELECT COALESCE(MAX(sort_order), -1) as m FROM totp_accounts',
    ).first<{ m: number }>();
    let sortBase = (maxRow?.m ?? -1) + 1;
    const now = Math.floor(Date.now() / 1000);
    let imported = 0;

    for (const raw of body.items) {
      const parsed = parseImportItem(raw);
      if (!parsed) continue;
      const normalized = normalizeBase32Secret(parsed.secret);
      try {
        await computeTotpCode(normalized, {
          digits: parsed.digits,
          period: parsed.period,
          algorithm: parsed.algorithm,
        });
      } catch {
        continue;
      }
      const id = crypto.randomUUID();
      const enc = await encryptSecret(c.env.MASTER_KEY, normalized);
      const shareToken = await newShareTokenForInsert(c.env.DB);
      await c.env.DB.prepare(
        `INSERT INTO totp_accounts (id, label, issuer, encrypted_secret, algorithm, digits, period, created_at, sort_order, share_token)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      )
        .bind(
          id,
          parsed.label,
          parsed.issuer,
          enc,
          parsed.algorithm.toUpperCase(),
          parsed.digits,
          parsed.period,
          now,
          sortBase++,
          shareToken,
        )
        .run();
      imported++;
    }

    return c.json({ ok: true, imported });
  });
}
