import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
  Alert,
  Badge,
  Button,
  Card,
  Divider,
  Group,
  Loader,
  Paper,
  SimpleGrid,
  Stack,
  Table,
  Text,
  ThemeIcon,
  Title,
  Tooltip,
  rem,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import {
  IconArrowRight,
  IconBook2,
  IconCertificate,
  IconClock,
  IconDownload,
  IconEye,
  IconInfoCircle,
  IconShieldCheck,
  IconTrash,
} from '@tabler/icons-react';
import { authStorage, useCurrentProfile } from '@ema-platform/auth';
import {
  extractErrorMessage,
  useApiQuery,
  useBypassPaymentMutation,
  useDiscardApplicationMutation,
  useGetPaymentCapabilitiesQuery,
} from '@ema-platform/api';
import {
  useGetMySeaServiceRecordsQuery,
  useGetMyMedicalCertificatesQuery,
} from '@ema-platform/api';
import { ConfirmModal, PdfPreviewModal } from '@ema-platform/ui';
import { useApplicationPayment } from '../../payments/hooks/useApplicationPayment';
import { examStageFor, registrationForApplication } from '../../licensing/exam-stage';
import type { MyRegistration } from '../../exams/pages/ExamsPage';

// ---------------------------------------------------------------------------
// Mock data
// ---------------------------------------------------------------------------
/** What `/certificates/my` returns: what is held, and what is still in flight. */
interface CertificatesOverview {
  certificates: {
    id: string;
    type: string;
    issued: string;
    expiry: string;
    status: string;
  }[];
  applications: {
    id: string;
    applicationId: string;
    type: string;
    /** Which license type this is a draft of — gates the Apply buttons. */
    licenseTypeKey: string | null;
    submitted: string;
    status: string;
    /** The fee owed at the current status, or null when nothing is due. */
    feeAmount: number | null;
    feeCurrency: string | null;
  }[];
}

/**
 * Badge colour per workflow status.
 *
 * Keyed by the values the API reports rather than display strings, so an
 * unmapped status falls back to grey instead of rendering colourless.
 */
const STATUS_COLOR: Record<string, string> = {
  DRAFT: 'gray',
  SUBMITTED: 'blue',
  UNDER_REVIEW: 'yellow',
  UNDER_EVALUATION: 'yellow',
  RESUBMIT_REQUIRED: 'orange',
  INSPECTION_PENDING: 'grape',
  INSPECTION_COMPLETED: 'grape',
  ELIGIBILITY_PAYMENT_PENDING: 'orange',
  ELIGIBILITY_PAID: 'blue',
  EXAM_PAYMENT_PENDING: 'orange',
  EXAM_PAID: 'blue',
  EXAM_SCHEDULED: 'indigo',
  EXAM_PASSED: 'teal',
  EXAM_FAILED: 'red',
  APPROVED: 'teal',
  REJECTED: 'red',
  PAYMENT_PENDING: 'orange',
  PAID: 'blue',
  PAYMENT_CONFIRMED: 'blue',
  CERTIFICATE_ISSUED: 'teal',
  COMPLETED: 'teal',
  ACTIVE: 'teal',
  EXPIRED: 'red',
  SUSPENDED: 'orange',
  CANCELLED: 'gray',
  SUPERSEDED: 'gray',
};

/**
 * What the examination leg reads as once the registration and attempt are
 * taken into account — see `examStageFor`. Plain strings rather than i18n
 * keys because this page states its statuses the same way.
 */
const EXAM_STAGE_LABELS: Record<string, string> = {
  ELIGIBLE_TO_REGISTER: 'Eligible — Register for a Sitting',
  REGISTERED: 'Exam Scheduled',
  ATTENDANCE_CONFIRMED: 'Exam Attendance Confirmed',
  SITTING: 'Exam In Progress',
  UNDER_EVALUATION: 'Exam Completed — Under Evaluation',
  PASSED: 'Passed — Certificate Fee Due',
  FAILED: 'Not Passed',
};

/** Turns `EXAM_PAYMENT_PENDING` into something a person reads. */
function humanStatus(status: string): string {
  return status
    .toLowerCase()
    .split('_')
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ');
}

function formatDate(value: string | null | undefined): string {
  if (!value) return '—';
  return new Date(value).toLocaleDateString('en-GB', {
    day: '2-digit',
    month: 'short',
    year: 'numeric',
  });
}

import { BASE_API_URL as API_BASE } from '@ema-platform/api';

async function generateCertificate(profileId: string): Promise<Blob> {
  const token = authStorage.getToken();
  if (!token) throw new Error('No auth token found');
  const res = await fetch(
    `${API_BASE}/profiles/generate-seafarer-certificate/${profileId}`,
    { headers: { Authorization: `Bearer ${token}` } },
  );
  if (!res.ok) throw new Error(`Failed to generate certificate (${res.status})`);
  return res.blob();
}

function downloadBlob(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}

export function CertificatesPage() {
  const navigate = useNavigate();
  const profileId = authStorage.getProfileId() ?? '';
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [previewTitle, setPreviewTitle] = useState('');
  const [loading, setLoading] = useState(false);
  const { pay, isPaying } = useApplicationPayment();
  // Dev/test only — the API reports false in production and the button is
  // never rendered. Same shortcut My Applications offers.
  const { data: capabilities } = useGetPaymentCapabilitiesQuery();
  const [bypassPayment, { isLoading: bypassing }] = useBypassPaymentMutation();
  const [discardApplication, { isLoading: discarding }] = useDiscardApplicationMutation();
  const [discardTarget, setDiscardTarget] = useState<{ id: string; applicationId: string } | null>(null);

  const { data, refetch } = useApiQuery<CertificatesOverview>({
    url: '/certificates/my',
    method: 'GET',
  });

  const handleBypass = async (applicationId: string) => {
    try {
      const result = await bypassPayment(applicationId).unwrap();
      notifications.show({
        color: 'teal',
        title: 'Payment bypassed',
        message: result.certificateIssued
          ? 'The certificate has been issued.'
          : `Application is now ${humanStatus(result.status)}.`,
      });
      // Generic query, not tag-driven: refresh it by hand.
      refetch();
    } catch (err) {
      notifications.show({ color: 'red', title: 'Bypass failed', message: extractErrorMessage(err) });
    }
  };
  const handleDiscard = async () => {
    if (!discardTarget) return;
    try {
      await discardApplication(discardTarget.applicationId).unwrap();
      notifications.show({
        color: 'teal',
        title: 'Draft discarded',
        message: `${discardTarget.id} has been deleted.`,
      });
      setDiscardTarget(null);
      refetch();
    } catch (err) {
      notifications.show({ color: 'red', title: 'Could not discard', message: extractErrorMessage(err) });
    }
  };

  const certificates = data?.certificates ?? [];
  const applications = data?.applications ?? [];

  // Registering, attendance and the sitting itself are recorded against the
  // registration, not the application — reading only the application status is
  // how this page could still say the candidate was waiting for an exam they
  // had already passed. Polled, because attendance is recorded while the
  // candidate is watching this screen.
  const { data: examRegistrations } = useApiQuery<MyRegistration[]>(
    { url: '/exams/registrations/mine', method: 'GET' },
    { pollingInterval: 30_000, refetchOnFocus: true },
  );

  /** Exam-leg applications read as the stage the records add up to. */
  const applicationStatusLabel = (app: { id: string; status: string }) => {
    const stage = examStageFor(
      app,
      registrationForApplication(app.id, examRegistrations),
    );
    return stage ? EXAM_STAGE_LABELS[stage] : humanStatus(app.status);
  };

  // eligibleForCoc is computed server-side (seafarer registration approved,
  // plus a verified sea service record and a verified medical certificate)
  // so the button and the API's own eligibility check can never disagree.
  // The three queries below only build the human-readable reason list for
  // the tooltip/banner — the gate itself is the one boolean.
  const { profile, eligibleForCoc: canApply } = useCurrentProfile();
  // One open draft per certificate type: the API resumes the existing draft
  // rather than stacking a second, so the button says so instead of looking
  // like it did nothing.
  const draftTypeKeys = new Set(
    applications.filter((a) => a.status === 'DRAFT').map((a) => a.licenseTypeKey),
  );
  const { data: seaServiceRecords } = useGetMySeaServiceRecordsQuery();
  const { data: medicalCertificates } = useGetMyMedicalCertificatesQuery();

  const seafarerApproved = profile?.seafarerStatus === 'ACTIVE';
  const hasVerifiedSeaService = (seaServiceRecords ?? []).some((r) => r.status === 'VERIFIED');
  const hasVerifiedMedical = (medicalCertificates ?? []).some((c) => c.status === 'VERIFIED');

  const missingReasons = [
    !seafarerApproved && 'Your seafarer registration is not yet approved.',
    !hasVerifiedSeaService && 'No verified sea service record on file.',
    !hasVerifiedMedical && 'No verified medical certificate on file.',
  ].filter((r): r is string => Boolean(r));

  const openPreview = async (profileId: string, title: string) => {
    setLoading(true);
    try {
      const blob = await generateCertificate(profileId);
      const url = URL.createObjectURL(blob);
      setPreviewTitle(title);
      setPreviewUrl(url);
    } catch (err) {
      notifications.show({
        color: 'red',
        title: 'Error',
        message: err instanceof Error ? err.message : 'Could not generate certificate',
      });
    } finally {
      setLoading(false);
    }
  };

  const handleDownload = async (profileId: string, title: string) => {
    try {
      const blob = await generateCertificate(profileId);
      downloadBlob(blob, `certificate-${Date.now()}.pdf`);
      notifications.show({
        color: 'teal',
        title: 'Downloaded',
        message: 'Certificate PDF downloaded successfully',
      });
    } catch (err) {
      notifications.show({
        color: 'red',
        title: 'Error',
        message: err instanceof Error ? err.message : 'Could not download certificate',
      });
    }
  };

  return (
    <Stack gap="md">
      <Group justify="space-between" align="flex-start" wrap="wrap" gap="sm">
        <div>
          <Title order={3}>Certificates (CoC / CoP)</Title>
          <Text fz="sm" c="dimmed">Certificate of Competency and Certificate of Proficiency under STCW</Text>
        </div>
        <Tooltip
          label={missingReasons.join(' ')}
          disabled={canApply}
          multiline
          w={260}
          events={{ hover: true, focus: true, touch: true }}
        >
          {/* Tooltip needs a hoverable child even while the button itself is
              disabled, so the reason still shows on hover. */}
          <span>
            <Group gap="xs">
              {([
                { key: 'CERTIFICATE_OF_COMPETENCY', label: 'CoC', variant: 'filled' as const },
                { key: 'CERTIFICATE_OF_PROFICIENCY', label: 'CoP', variant: 'light' as const },
              ]).map(({ key, label, variant }) => {
                const hasDraft = draftTypeKeys.has(key);
                return (
                  <Button
                    key={key}
                    variant={variant}
                    leftSection={<IconShieldCheck size={15} />}
                    rightSection={<IconArrowRight size={15} />}
                    onClick={() => navigate(`/licensing/${key}/apply`)}
                    disabled={!canApply || hasDraft}
                    title={hasDraft ? `You already have a ${label} draft — continue it below.` : undefined}
                  >
                    {`Apply for ${label}`}
                  </Button>
                );
              })}
            </Group>
          </span>
        </Tooltip>
      </Group>

      {!canApply && (
        <Alert variant="light" color="orange" icon={<IconInfoCircle size={15} />}>
          <Text fz="sm" fw={600}>Not yet eligible to apply</Text>
          <Text fz="xs" c="dimmed">{missingReasons.join(' ')}</Text>
        </Alert>
      )}

      {/* Info banner */}
      <Paper withBorder radius="lg" p="md" bg="var(--mantine-color-blue-light)">
        <Group gap="md" wrap="nowrap">
          <ThemeIcon size={48} radius="md" color="blue" variant="light"><IconShieldCheck size={24} /></ThemeIcon>
          <Stack gap={2} style={{ flex: 1 }}>
            <Text fw={700} fz="sm">What is a CoC / CoP?</Text>
            <SimpleGrid cols={{ base: 1, sm: 3 }} spacing="xs">
              {[
                { icon: IconBook2,        color: 'blue',  title: 'Certificate of Competency (CoC)', desc: 'Authorizes the holder to serve as an officer or master on board a ship under STCW.' },
                { icon: IconCertificate,  color: 'teal',  title: 'Certificate of Proficiency (CoP)', desc: 'Certifies completion of specific STCW training for specialized duties on board.' },
                { icon: IconClock,        color: 'orange', title: 'Validity', desc: 'CoC/CoP certificates are valid for 5 years and must be revalidated before expiry.' },
              ].map(({ icon: Icon, color, title, desc }) => (
                <Card key={title} withBorder radius="md" p="sm">
                  <Group gap="xs" mb={4}>
                    <ThemeIcon size={20} radius="sm" color={color} variant="light"><Icon size={12} /></ThemeIcon>
                    <Text fz="xs" fw={700}>{title}</Text>
                  </Group>
                  <Text fz="xs" c="dimmed" lh={1.4}>{desc}</Text>
                </Card>
              ))}
            </SimpleGrid>
          </Stack>
        </Group>
      </Paper>

      {/* Active applications */}
      <Paper withBorder radius="lg" p="xl">
        <Text fw={700} mb="md">My Applications</Text>
        {applications.length === 0 ? (
          <Alert variant="light" color="gray" icon={<IconInfoCircle size={15} />}>
            No active CoC/CoP applications. Click "Apply for CoC" or "Apply for CoP" to start.
          </Alert>
        ) : (
          <Table highlightOnHover fz="sm" verticalSpacing="sm">
            <Table.Thead bg="var(--mantine-color-default-hover)">
              <Table.Tr>
                {['App ID', 'Certificate Type', 'Submitted', 'Exam Date / Status Note', 'Status', ''].map((h) => (
                  <Table.Th key={h} style={{ fontSize: rem(11), textTransform: 'uppercase', color: 'var(--mantine-color-dimmed)' }}>{h}</Table.Th>
                ))}
              </Table.Tr>
            </Table.Thead>
            <Table.Tbody>
              {applications.map((app) => (
                <Table.Tr key={app.id}>
                  <Table.Td><Text fz="xs" fw={600} c="blue.7">{app.id}</Text></Table.Td>
                  <Table.Td><Text fz="xs" fw={500} maw={220} style={{ lineHeight: 1.3 }}>{app.type}</Text></Table.Td>
                  <Table.Td><Text fz="xs">{formatDate(app.submitted)}</Text></Table.Td>
                  <Table.Td>
                    <Text fz="xs" c="dimmed" maw={200} lh={1.3}>
                      {applicationStatusLabel(app)}
                    </Text>
                  </Table.Td>
                  <Table.Td>
                    <Badge
                      color={STATUS_COLOR[app.status] ?? 'gray'}
                      variant="light"
                      size="sm"
                    >
                      {applicationStatusLabel(app)}
                    </Badge>
                  </Table.Td>
                  <Table.Td>
                    <Group gap="xs" justify="flex-end" wrap="nowrap">
                      {/* feeAmount is non-null exactly when the server would
                          accept a payment, so the button and the API agree. */}
                      {app.feeAmount !== null && (
                        <Button
                          size="xs"
                          color="yellow"
                          loading={isPaying}
                          onClick={() => pay(app.applicationId)}
                        >
                          Pay {app.feeAmount.toLocaleString()} {app.feeCurrency}
                        </Button>
                      )}
                      {app.feeAmount !== null && capabilities?.bypassEnabled && (
                        <Button
                          size="xs"
                          variant="default"
                          loading={bypassing}
                          onClick={() => handleBypass(app.applicationId)}
                          title="Testing only — marks the fee paid without a provider"
                        >
                          Bypass payment
                        </Button>
                      )}
                      <Text
                        fz="xs"
                        c="blue"
                        style={{ cursor: 'pointer' }}
                        onClick={() => navigate(`/applications/${app.applicationId}`)}
                      >
                        {app.status === 'DRAFT' ? 'Continue' : 'Details'}
                      </Text>
                      {/* Drafts only: once submitted the filing is a record,
                          and withdrawing it is the officer's call. */}
                      {app.status === 'DRAFT' && (
                        <Button
                          size="xs"
                          variant="subtle"
                          color="red"
                          leftSection={<IconTrash size={13} />}
                          onClick={() => setDiscardTarget({ id: app.id, applicationId: app.applicationId })}
                        >
                          Discard
                        </Button>
                      )}
                    </Group>
                  </Table.Td>
                </Table.Tr>
              ))}
            </Table.Tbody>
          </Table>
        )}
      </Paper>

      {/* Issued certificates */}
      <Paper withBorder radius="lg" p="xl">
        <Text fw={700} mb="md">My Certificates</Text>
        {certificates.length === 0 ? (
          <Alert variant="light" color="gray" icon={<IconInfoCircle size={15} />}>
            No certificates issued yet.
          </Alert>
        ) : (
          <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
            {certificates.map((cert) => (
              <Card key={cert.id} withBorder radius="md" p="md">
                <Group justify="space-between" mb="sm">
                  <Group gap="sm">
                    <ThemeIcon size={36} radius="md" color="blue" variant="light"><IconShieldCheck size={18} /></ThemeIcon>
                    <div>
                      <Text fw={700} fz="sm">{cert.type}</Text>
                      <Text fz="xs" c="dimmed">{cert.id}</Text>
                    </div>
                  </Group>
                  <Badge color={STATUS_COLOR[cert.status] ?? "gray"} variant="light">{cert.status}</Badge>
                </Group>
                <Divider mb="sm" />
                <SimpleGrid cols={2} spacing="xs">
                  <div><Text fz="xs" c="dimmed">Issued</Text><Text fz="sm" fw={500}>{cert.issued}</Text></div>
                  <div><Text fz="xs" c="dimmed">Expires</Text><Text fz="sm" fw={500}>{cert.expiry}</Text></div>
                </SimpleGrid>
                <Group mt="sm" gap="xs">
                  <Button size="xs" variant="light" leftSection={loading ? <Loader size={12} /> : <IconEye size={12} />} onClick={() => openPreview(profileId, cert.type)}>View</Button>
                  <Button size="xs" variant="default" leftSection={<IconDownload size={12} />} onClick={() => handleDownload(profileId, cert.type)}>Download</Button>
                </Group>
              </Card>
            ))}
          </SimpleGrid>
        )}
      </Paper>

      <ConfirmModal
        opened={!!discardTarget}
        onClose={() => setDiscardTarget(null)}
        onConfirm={handleDiscard}
        loading={discarding}
        title="Discard draft"
        message={`Delete draft ${discardTarget?.id ?? ''}? Anything filled in so far is lost.`}
        confirmLabel="Discard"
      />

      <PdfPreviewModal
        opened={!!previewUrl}
        onClose={() => setPreviewUrl(null)}
        url={previewUrl ?? ''}
        title={previewTitle}
      />
    </Stack>
  );
}
