'use client';

import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import {
  Euro, CheckCircle2, Loader2, ExternalLink, ShieldCheck,
  Sparkles, ArrowLeft, GitBranch,
} from 'lucide-react';
import { useSession } from '../lib/session-context';
import { getPlanCapabilities } from '../lib/plan-access';
import { hasUnlimitedCredit } from '../lib/unlimited-credit';
import type { Project } from '../data/projectsData';
import {
  estimateConversionTokens,
  grossEurCentsFromTokens,
} from '../lib/conversion-pricing';
import {
  RISK_MULTIPLIERS,
  RISK_LABELS,
  computeFeatureFactors,
  featureMultiplierFromFactors,
  type FeatureFactor,
} from '../lib/cost-approval-factors';

function tokensToEur(tokens: number): number {
  return grossEurCentsFromTokens(tokens) / 100;
}

function formatEur(amount: number): string {
  return new Intl.NumberFormat('de-DE', {
    style: 'currency',
    currency: 'EUR',
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(amount);
}


// ── Bytes → LOC (avg ~45 bytes per line across languages) ────────────────────
const BYTES_PER_LINE = 45;

function bytesToLoc(bytes: number): number {
  return Math.round(bytes / BYTES_PER_LINE);
}

function parseRepoOwnerRepo(url: string): { owner: string; repo: string } | null {
  const m = url.match(/github\.com\/([^/]+)\/([^/.\s]+)/);
  if (!m) return null;
  return { owner: m[1], repo: m[2] };
}

interface Props {
  project: Project | null;
  /** Receives the exact token figure shown to the user so the billed price matches. */
  onConfirm: (estimatedTokens: number) => Promise<void> | void;
  onReject?: () => void;
}

export default function CostApprovalGate({ project, onConfirm, onReject }: Props) {
  const { user: sessionUser } = useSession();
  const [accepted, setAccepted] = useState(false);
  const [confirming, setConfirming] = useState(false);
  const [creditsRemaining, setCreditsRemaining] = useState<number | null>(null);
  const [loading, setLoading] = useState(true);

  // Repo-derived LOC state (GitHub)
  const [repoLoc, setRepoLoc] = useState<number | null>(null);
  const [repoLocLoading, setRepoLocLoading] = useState(false);
  const [repoLocSource, setRepoLocSource] = useState<string | null>(null);
  // Upload-derived LOC state (custom folder / uploaded archive)
  const [uploadLoc, setUploadLoc] = useState<number | null>(null);
  const [uploadLocLoading, setUploadLocLoading] = useState(false);

  // ── Load credits ────────────────────────────────────────────────────────────
  useEffect(() => {
    const companyId = sessionUser?.companyId;
    const planCredits = getPlanCapabilities(sessionUser?.tier).includedCreditsTokens;
    const load = async () => {
      if (!companyId) { setCreditsRemaining(planCredits); setLoading(false); return; }
      try {
        const r = await fetch(`/api/companies/${companyId}/tokens`, { credentials: 'include' });
        const d = r.ok ? await r.json() : null;
        setCreditsRemaining(typeof d?.tokens?.remaining === 'number' ? d.tokens.remaining : planCredits);
      } catch {
        setCreditsRemaining(planCredits);
      } finally {
        setLoading(false);
      }
    };
    void load();
  }, [sessionUser?.companyId, sessionUser?.tier]);

  // ── Fetch LOC from GitHub when repoUrl is available ─────────────────────────
  const repoUrl = project?.repoUrl || (typeof (project?.config as Record<string, unknown> | undefined)?.repoUrl === 'string'
    ? (project?.config as Record<string, unknown>).repoUrl as string
    : undefined);

  useEffect(() => {
    if (!repoUrl) return;
    // Only fetch if we don't already have better data (totalLines from pre-analysis)
    if (project?.totalLines && project.totalLines > 0) return;
    const parsed = parseRepoOwnerRepo(repoUrl);
    if (!parsed) return;

    setRepoLocLoading(true);
    const sourceId = project?.sourceLanguage?.toLowerCase();

    fetch(`/api/github/languages?owner=${encodeURIComponent(parsed.owner)}&repo=${encodeURIComponent(parsed.repo)}`, {
      credentials: 'include',
    })
      .then(r => r.ok ? r.json() : null)
      .then((data: { breakdown?: { scribaId: string | null; bytes: number; name: string }[]; totalBytes?: number } | null) => {
        if (!data) return;

        // Try to match the project's source language
        const match = data.breakdown?.find(
          b => b.scribaId === sourceId || b.name.toLowerCase() === sourceId
        );

        if (match && match.bytes > 0) {
          setRepoLoc(bytesToLoc(match.bytes));
          setRepoLocSource(`${match.name} bytes from GitHub`);
        } else if (data.totalBytes && data.totalBytes > 0) {
          // Fall back: assume ~35% of total repo bytes is migratable source
          setRepoLoc(bytesToLoc(Math.round(data.totalBytes * 0.35)));
          setRepoLocSource('estimated from total repo size');
        }
      })
      .catch(() => {/* silently skip if GitHub not connected */})
      .finally(() => setRepoLocLoading(false));
  }, [repoUrl, project?.sourceLanguage, project?.totalLines]);

  // ── Auto-analyze upload to get LOC when no other source is available ─────────
  useEffect(() => {
    if (!project?.id) return;
    if (project.totalLines && project.totalLines > 0) return; // DB already has it
    if (repoUrl) return; // GitHub path handles it
    const cfg = (project.config ?? {}) as Record<string, unknown>;
    if (!cfg.uploadId) return; // no upload to analyze

    setUploadLocLoading(true);
    fetch(`/api/conversions/${project.id}/analyze-upload`, {
      method: 'POST',
      credentials: 'include',
    })
      .then(r => r.ok ? r.json() : null)
      .then((data: { scanResults?: { totalLines?: number } } | null) => {
        const lines = data?.scanResults?.totalLines;
        if (lines && lines > 0) setUploadLoc(lines);
      })
      .catch(() => {/* silently skip */})
      .finally(() => setUploadLocLoading(false));
  }, [project?.id, project?.totalLines, project?.config, repoUrl]);

  // ── Calculation ─────────────────────────────────────────────────────────────
  const projectConfig = (project?.config ?? {}) as Record<string, unknown>;
  const scanResults = (projectConfig.preAnalysis as Record<string, unknown> | undefined)?.scanResults as Record<string, unknown> | undefined;
  // effLOC = code lines only (no blanks/comments) from pre-analysis — the pricing basis.
  const effectiveLoc =
    typeof scanResults?.effectiveLoc === 'number' && scanResults.effectiveLoc > 0
      ? (scanResults.effectiveLoc as number)
      : undefined;

  // Prefer: actual token usage → pre-analysis effLOC → upload-analyzed LOC → GitHub LOC → wizard estimatedLOC → fallback
  const baseTokens = estimateConversionTokens({
    tokensUsed: project?.tokensUsed,
    effectiveLoc,
    totalLines: project?.totalLines,
    sourceLanguage: project?.sourceLanguage,
    estimatedLOC: uploadLoc
      ?? (repoLoc ?? undefined)
      ?? (projectConfig.estimatedLOC as number | string | null | undefined),
  });

  const featureFactors = computeFeatureFactors(projectConfig);
  const featureMultiplier = featureMultiplierFromFactors(featureFactors);

  const riskLevel = typeof projectConfig.riskLevel === 'string' ? projectConfig.riskLevel : 'medium';
  const riskMultiplier = RISK_MULTIPLIERS[riskLevel] ?? 1.0;

  const estimatedTokens = Math.round(baseTokens * featureMultiplier * riskMultiplier);

  // LOC used for the breakdown display — show the same effLOC (code lines) the price is based on.
  const locSource = effectiveLoc
    ?? project?.totalLines
    ?? uploadLoc
    ?? repoLoc
    ?? (typeof projectConfig.estimatedLOC === 'string'
        ? parseInt(projectConfig.estimatedLOC, 10)
        : (projectConfig.estimatedLOC as number | null | undefined) ?? null);
  const locIsFromRepo = !effectiveLoc && !project?.totalLines && !uploadLoc && repoLoc != null && repoLoc > 0;
  const locIsFromUpload = !effectiveLoc && !project?.totalLines && uploadLoc != null && uploadLoc > 0;
  const locIsLoading = repoLocLoading || uploadLocLoading;

  const unlimited = hasUnlimitedCredit(sessionUser?.email);
  const grossCost = tokensToEur(estimatedTokens);
  const creditValue = unlimited ? grossCost : (creditsRemaining !== null ? tokensToEur(creditsRemaining) : 0);
  const appliedCredits = Math.min(creditValue, grossCost);
  const netCost = unlimited ? 0 : Math.max(0, grossCost - appliedCredits);
  const hasCredits = unlimited || (creditsRemaining !== null && creditsRemaining > 0);

  if (loading) {
    return (
      <div className="flex items-center justify-center p-16">
        <Loader2 className="w-6 h-6 animate-spin text-accent-light" />
      </div>
    );
  }

  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      className="max-w-xl mx-auto space-y-4"
    >
      {/* Hero */}
      <div className="glass rounded-2xl p-8 text-center">
        <div className="w-16 h-16 rounded-full bg-amber-500/15 flex items-center justify-center mx-auto mb-5">
          <Euro className="w-8 h-8 text-amber-400" />
        </div>
        <h3 className="text-xl font-bold text-foreground mb-2">Migration Cost</h3>
        <p className="text-sm text-muted">
          Final price for{' '}
          <span className="font-semibold text-foreground">{project?.name ?? 'this conversion'}</span>
          {' '}— based on your selected project options.
        </p>
        <div className="mt-4 flex items-start gap-2 bg-green-500/8 border border-green-500/20 rounded-lg px-4 py-3 text-left">
          <ShieldCheck className="w-4 h-4 text-green-400 shrink-0 mt-0.5" />
          <p className="text-[11px] text-green-300/80 leading-relaxed">
            This is a <span className="font-semibold text-green-300">fixed price</span> — the exact amount that will be billed for this conversion.
            It is computed from your project options and{' '}
            {repoUrl ? 'repository data' : 'the values you entered'}.
          </p>
        </div>
      </div>

      {/* Quotation breakdown */}
      <div className="glass rounded-xl p-6 space-y-3">
        <h4 className="text-xs font-semibold text-muted uppercase tracking-wider mb-4">Price Breakdown</h4>

        {/* Inputs used */}
        <div className="bg-border/20 rounded-lg px-3 py-3 space-y-1.5 mb-3">
          <p className="text-[10px] text-muted uppercase tracking-wider font-semibold mb-2">Based on your settings</p>
          <div className="grid grid-cols-2 gap-x-4 gap-y-1">
            {project?.sourceLanguage && (
              <div className="flex items-center gap-1.5 text-[11px] text-muted">
                <span className="w-1.5 h-1.5 rounded-full bg-accent-light/60 shrink-0" />
                Source: <span className="text-foreground font-medium ml-1">{project.sourceLanguage}</span>
              </div>
            )}
            {project?.targetLanguage && (
              <div className="flex items-center gap-1.5 text-[11px] text-muted">
                <span className="w-1.5 h-1.5 rounded-full bg-accent-light/60 shrink-0" />
                Target: <span className="text-foreground font-medium ml-1">{project.targetLanguage}</span>
              </div>
            )}
            {locSource != null && locSource > 0 && (
              <div className="flex items-center gap-1.5 text-[11px] text-muted">
                <span className="w-1.5 h-1.5 rounded-full bg-accent-light/60 shrink-0" />
                Code lines:
                <span className="text-foreground font-medium ml-1">
                  {(locSource as number).toLocaleString('de-DE')}
                  {locIsFromRepo && repoLocSource && (
                    <span className="ml-1 text-[9px] text-accent-light/70 font-normal">(from repo)</span>
                  )}
                  {locIsFromUpload && (
                    <span className="ml-1 text-[9px] text-accent-light/70 font-normal">(from upload)</span>
                  )}
                </span>
              </div>
            )}
            {locIsLoading && (
              <div className="flex items-center gap-1.5 text-[11px] text-muted">
                <Loader2 className="w-2.5 h-2.5 animate-spin" />
                {uploadLocLoading ? 'Analysing source files…' : 'Fetching repo LOC…'}
              </div>
            )}
            <div className="flex items-center gap-1.5 text-[11px] text-muted">
              <span className="w-1.5 h-1.5 rounded-full bg-accent-light/60 shrink-0" />
              Risk: <span className="text-foreground font-medium ml-1">{RISK_LABELS[riskLevel] ?? riskLevel}</span>
            </div>
            {repoUrl && (
              <div className="col-span-2 flex items-center gap-1.5 text-[11px] text-muted">
                <GitBranch className="w-3 h-3 text-accent-light/60 shrink-0" />
                <span className="truncate max-w-[280px]">{repoUrl}</span>
              </div>
            )}
          </div>
        </div>

        {/* Features factored in */}
        <div className="bg-border/20 rounded-lg px-3 py-3 mb-3">
          <p className="text-[10px] text-muted uppercase tracking-wider font-semibold mb-2">Features factored in</p>
          <div className="space-y-1">
            {featureFactors.map((f, i) => (
              <div key={i} className="flex items-center justify-between text-[11px]">
                <span className="text-muted">{f.label}</span>
                <span className="font-mono text-foreground/70">+{Math.round(f.delta * 100)}%</span>
              </div>
            ))}
            {riskMultiplier !== 1 && (
              <div className="flex items-center justify-between text-[11px] border-t border-border/50 pt-1 mt-1">
                <span className="text-muted">Risk adjustment ({riskLevel})</span>
                <span className={`font-mono ${riskMultiplier > 1 ? 'text-amber-400' : 'text-green-400'}`}>
                  {riskMultiplier > 1 ? '+' : ''}{Math.round((riskMultiplier - 1) * 100)}%
                </span>
              </div>
            )}
          </div>
        </div>

        <div className="flex items-center justify-between text-sm">
          <span className="text-muted">List price</span>
          <span className="font-mono font-semibold text-foreground">
            {locIsLoading ? <Loader2 className="w-3.5 h-3.5 animate-spin inline" /> : <>{formatEur(grossCost)}</>}
          </span>
        </div>

        {hasCredits && (
          <div className="flex items-center justify-between text-sm">
            <span className="text-muted flex items-center gap-1.5">
              <Sparkles className="w-3.5 h-3.5 text-green-400" />
              {unlimited ? 'Unlimited credit applied' : 'Plan credit applied'}
            </span>
            <span className="font-mono font-semibold text-green-400">−{formatEur(appliedCredits)}</span>
          </div>
        )}

        <div className="h-px bg-border my-1" />

        <div className="flex items-center justify-between">
          <div>
            <span className="text-sm font-bold text-foreground">Total price</span>
          </div>
          <span className="text-2xl font-bold text-foreground">
            {locIsLoading ? <Loader2 className="w-5 h-5 animate-spin inline" /> : <>{formatEur(netCost)}</>}
          </span>
        </div>

        <p className="text-[11px] text-muted pt-1 leading-relaxed">
          {unlimited
            ? 'This account has unlimited credit — this conversion is included at no charge.'
            : hasCredits
              ? `Your remaining plan credit (${formatEur(creditValue)}) is applied to this conversion's price.`
              : 'This price is based on your selected project options.'}
          {' '}It includes core migration, AI scaffold generation, and all enabled output features.
          This is the exact amount billed; billing runs on the 1st of each month for all conversions completed that month, minus the available plan credit.
        </p>
      </div>

      {/* Confirmation */}
      <div className="glass rounded-xl p-6 space-y-4">
        <p className="text-sm font-semibold text-foreground">Do you accept this price?</p>

        <label className="flex items-start gap-3 cursor-pointer select-none group" onClick={() => setAccepted((a) => !a)}>
          <div
            className={`mt-0.5 w-5 h-5 rounded border-2 flex items-center justify-center shrink-0 transition-all ${
              accepted ? 'bg-accent border-accent' : 'border-border group-hover:border-accent/60'
            }`}
          >
            {accepted && <CheckCircle2 className="w-3.5 h-3.5 text-white" />}
          </div>
          <span className="text-sm text-foreground leading-relaxed">
            I accept this price and the{' '}
            <a
              href="#"
              onClick={(e) => e.stopPropagation()}
              className="text-accent-light hover:underline inline-flex items-center gap-0.5"
            >
              terms and conditions <ExternalLink className="w-3 h-3" />
            </a>
          </span>
        </label>

        <button
          onClick={async () => {
            if (!accepted || confirming) return;
            setConfirming(true);
            try { await onConfirm(estimatedTokens); } finally { setConfirming(false); }
          }}
          disabled={!accepted || confirming}
          className="w-full py-3 rounded-lg gradient-accent text-white text-sm font-semibold hover:opacity-90 transition-opacity flex items-center justify-center gap-2 disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer glow-accent"
        >
          {confirming ? (
            <><span className="w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" /> Processing...</>
          ) : (
            <><ShieldCheck className="w-4 h-4" /> Accept &amp; Proceed</>
          )}
        </button>

        {onReject && (
          <button
            onClick={onReject}
            className="w-full py-2.5 rounded-lg border border-border text-sm text-muted hover:text-foreground hover:border-accent/40 transition-colors flex items-center justify-center gap-2 cursor-pointer"
          >
            <ArrowLeft className="w-3.5 h-3.5" /> No, go back to strategy
          </button>
        )}
      </div>

      {netCost === 0 && hasCredits && (
        <p className="text-center text-xs text-green-400 font-medium flex items-center justify-center gap-1.5">
          <Sparkles className="w-3.5 h-3.5" />
          {unlimited
            ? 'This account has unlimited credit — no charge applies.'
            : 'This conversion is fully covered by your plan credits — no charge applies.'}
        </p>
      )}
    </motion.div>
  );
}
