import { NextRequest, NextResponse } from 'next/server';
import { db, schema, dbHelpers } from '@/lib/db';
import { getSession } from '@/lib/auth';
import { eq, and, sql, gte } from 'drizzle-orm';
import {
  applyLicenseCreditsToEstimate,
  estimateConversionTokens,
  grossEurCentsFromTokens,
} from '@/lib/conversion-pricing';
import { hasUnlimitedCredit } from '@/lib/unlimited-credit';

async function authorizeConversion(request: NextRequest, conversionId: string) {
  const accessToken = request.cookies.get('scriba.access_token')?.value;
  const session = await getSession(accessToken);
  if (!session) {
    return { error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) };
  }
  const row = await dbHelpers.getProject(conversionId);
  if (!row) {
    return { error: NextResponse.json({ error: 'Conversion not found' }, { status: 404 }) };
  }
  const isAdmin = session.user.role === 'admin';
  if (!isAdmin && row.userId !== session.user.id) {
    return { error: NextResponse.json({ error: 'Forbidden' }, { status: 403 }) };
  }
  return { session, row, isAdmin };
}

/**
 * POST /api/conversions/[id]/approve-cost
 * Locks in upfront € estimate, deducts plan credits (tokens) from the company pool, sets config.costApproved.
 */
export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> },
) {
  try {
    const { id } = await params;
    const auth = await authorizeConversion(request, id);
    if ('error' in auth) return auth.error;

    const row = auth.row;
    if (row.costApprovedAt != null && row.approvedNetEurCents != null) {
      return NextResponse.json({
        success: true,
        alreadyApproved: true,
        breakdown: {
          estimatedTokens: row.approvedEstimatedTokens,
          grossEurCents: row.approvedGrossEurCents,
          netEurCents: row.approvedNetEurCents,
          creditsTokensApplied: row.approvedCreditsTokensApplied ?? 0,
        },
      });
    }

    const rowConfig = (row.config && typeof row.config === 'object' && !Array.isArray(row.config))
      ? (row.config as Record<string, unknown>)
      : {};
    // The gate sends the exact token figure it displayed and the user accepted, so the
    // billed price matches the shown price. Fall back to the server estimate otherwise.
    const body = await request.json().catch(() => ({} as { estimatedTokens?: unknown }));
    const overrideTokens =
      typeof body?.estimatedTokens === 'number' && body.estimatedTokens > 0
        ? Math.round(body.estimatedTokens)
        : null;

    const scanResults = (rowConfig.preAnalysis as Record<string, unknown> | undefined)?.scanResults as Record<string, unknown> | undefined;
    const effectiveLoc =
      typeof scanResults?.effectiveLoc === 'number' && scanResults.effectiveLoc > 0
        ? (scanResults.effectiveLoc as number)
        : undefined;
    const estimatedTokens = overrideTokens ?? estimateConversionTokens({
      tokensUsed: row.tokensUsed,
      effectiveLoc,
      totalLines: row.totalLines,
      sourceLanguage: row.sourceLanguage,
      estimatedLOC: rowConfig.estimatedLOC as number | string | null | undefined,
    });

    // Unlimited-credit accounts (demo/internal) are never charged: snapshot a fully
    // credited approval (net 0) and skip any company license-credit deduction.
    const projectOwner = auth.row.userId
      ? await db.query.users.findFirst({
          where: eq(schema.users.id, auth.row.userId),
          columns: { email: true },
        })
      : null;
    if (hasUnlimitedCredit(projectOwner?.email)) {
      const grossEurCents = grossEurCentsFromTokens(estimatedTokens);
      const prev = (row.config && typeof row.config === 'object' && !Array.isArray(row.config))
        ? (row.config as Record<string, unknown>)
        : {};
      await db
        .update(schema.projects)
        .set({
          approvedEstimatedTokens: estimatedTokens,
          approvedGrossEurCents: grossEurCents,
          approvedNetEurCents: 0,
          approvedCreditsTokensApplied: estimatedTokens,
          costApprovedAt: new Date(),
          updatedAt: new Date(),
          config: { ...prev, costApproved: true },
        })
        .where(eq(schema.projects.id, id));
      return NextResponse.json({ success: true, unlimited: true });
    }

    // Resolve the company to deduct credits from.
    // Admins bypass billing entirely — approve at gross cost with no credit deduction.
    let companyId = auth.session.user.companyId;
    if (!companyId && auth.isAdmin && auth.row.userId) {
      const owner = await db.query.users.findFirst({
        where: eq(schema.users.id, auth.row.userId),
        columns: { companyId: true },
      });
      companyId = owner?.companyId ?? null;
    }

    const prevCfg =
      row.config && typeof row.config === 'object' && !Array.isArray(row.config)
        ? (row.config as Record<string, unknown>)
        : {};

    if (!companyId) {
      // Admin approving a conversion with no company on either side — skip credit deduction.
      const grossEurCents = grossEurCentsFromTokens(estimatedTokens);
      await db
        .update(schema.projects)
        .set({
          approvedEstimatedTokens: estimatedTokens,
          approvedGrossEurCents: grossEurCents,
          approvedNetEurCents: grossEurCents,
          approvedCreditsTokensApplied: 0,
          costApprovedAt: new Date(),
          updatedAt: new Date(),
          config: { ...prevCfg, costApproved: true },
        })
        .where(eq(schema.projects.id, id));
      return NextResponse.json({ success: true });
    }

    await db.transaction(async (tx) => {
      const [company] = await tx
        .select({ licenseCreditsRemaining: schema.companies.licenseCreditsRemaining })
        .from(schema.companies)
        .where(eq(schema.companies.id, companyId!));

      if (!company) {
        throw new Error('Company not found');
      }

      const { grossEurCents, creditsTokensApplied, netEurCents } = applyLicenseCreditsToEstimate(
        estimatedTokens,
        company.licenseCreditsRemaining,
      );

      const whereParts = [eq(schema.companies.id, companyId!)];
      if (creditsTokensApplied > 0) {
        whereParts.push(gte(schema.companies.licenseCreditsRemaining, creditsTokensApplied));
      }

      const dec = await tx
        .update(schema.companies)
        .set({
          licenseCreditsRemaining: sql`${schema.companies.licenseCreditsRemaining} - ${creditsTokensApplied}`,
          updatedAt: new Date(),
        })
        .where(and(...whereParts))
        .returning({ id: schema.companies.id });

      if (dec.length === 0 && creditsTokensApplied > 0) {
        throw new Error('CREDIT_RACE');
      }

      await tx
        .update(schema.projects)
        .set({
          approvedEstimatedTokens: estimatedTokens,
          approvedGrossEurCents: grossEurCents,
          approvedNetEurCents: netEurCents,
          approvedCreditsTokensApplied: creditsTokensApplied,
          costApprovedAt: new Date(),
          updatedAt: new Date(),
          config: { ...prevCfg, costApproved: true },
        })
        .where(eq(schema.projects.id, id));
    });

    return NextResponse.json({ success: true });
  } catch (error) {
    if (error instanceof Error && error.message === 'CREDIT_RACE') {
      return NextResponse.json({ error: 'Could not apply credits; please retry.' }, { status: 409 });
    }
    if (error instanceof Error && error.message === 'Company not found') {
      return NextResponse.json({ error: 'Company not found' }, { status: 404 });
    }
    console.error('approve-cost error:', error);
    return NextResponse.json({ error: 'Failed to approve conversion cost' }, { status: 500 });
  }
}
