import React, { useCallback, useEffect, useState } from "react";

import {
  Alert, Box, Button, Chip, Divider, FormControl, IconButton, InputLabel, MenuItem, Paper, Select,
  Skeleton, Stack, TextField, Tooltip, Typography,
} from "@mui/material";
import RichTextField from "./RichTextField";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import RestoreIcon from "@mui/icons-material/Restore";
import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";

import { getApiErrorMessage } from "../api";
import {
  ApplicationCv, CoverLetter, TailoringPlan, applicationAssetsApi,
} from "../applicationWorkspace";
import { cvBuilderApi } from "../cvBuilder";
import { aiWorkspaceApi } from "../aiWorkspace";
import { useAccountPlan } from "../accountPlan";
import { useI18n } from "../i18n/I18nProvider";

// Phase 5.4 — Application Assets sections for the workspace.
//
// These compose systems that already exist. CV editing, preview, PDF export, themes and version
// history all live in the CV builder at /cv-builder — this section only chooses WHICH variant the
// application uses and links out. The cover letter is the one thing genuinely owned here, because it
// is application-specific by nature. docs/architecture/application-workspace.md.

function useAsset<T>(load: () => Promise<T>, deps: React.DependencyList) {
  const { t } = useI18n();
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const run = useCallback(load, deps);

  const reload = useCallback(() => {
    let cancelled = false;
    setLoading(true);
    run()
      .then((r) => {
        if (!cancelled) {
          setData(r);
          setError(null);
        }
      })
      .catch((err) => {
        if (!cancelled) setError(getApiErrorMessage(err, t("assetsLoadFailed")));
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [run, t]);

  useEffect(() => reload(), [reload]);

  return { data, error, loading, setData, setError, reload };
}

function Shell({ title, subtitle, loading, error, children }: {
  title: string;
  subtitle?: string;
  loading: boolean;
  error: string | null;
  children: React.ReactNode;
}) {
  return (
    <Paper sx={{ p: { xs: 2, sm: 2.5 }, borderRadius: 3 }}>
      <Typography variant="subtitle2" sx={{ fontWeight: 800 }}>{title}</Typography>
      {subtitle && <Typography variant="caption" color="text.secondary">{subtitle}</Typography>}
      <Divider sx={{ my: 1.5 }} />
      {loading ? (
        <Stack spacing={1}>{[0, 1, 2].map((i) => <Skeleton key={i} variant="rounded" height={44} />)}</Stack>
      ) : error ? (
        <Alert severity="error">{error}</Alert>
      ) : (
        children
      )}
    </Paper>
  );
}

// ---------- CV ----------

export function ApplicationCvSection({ jobId }: { jobId: number }) {
  const { t } = useI18n();
  const { data, error, loading, setData, setError } = useAsset<ApplicationCv>(
    () => applicationAssetsApi.cv(jobId),
    [jobId],
  );
  const [busy, setBusy] = useState(false);

  const attach = async (variantId: number | null) => {
    setBusy(true);
    try {
      setData(await applicationAssetsApi.attachVariant(jobId, variantId));
      setError(null);
    } catch (err) {
      setError(getApiErrorMessage(err, t("assetsAttachCvFailed")));
    } finally {
      setBusy(false);
    }
  };

  const attached = data?.attachedVariantId ?? "";

  const duplicateForJob = async () => {
    if (!data?.attachedVariantId) return;
    setBusy(true);
    try {
      const copy = await cvBuilderApi.duplicate(data.attachedVariantId, t("assetsTailoredCopyName", { name: data.attachedVariantName || "CV" }));
      setData(await applicationAssetsApi.attachVariant(jobId, copy.id));
      window.location.assign(`/career/builder/${copy.id}`);
    } catch (err) {
      setError(getApiErrorMessage(err, t("assetsDuplicateCvFailed")));
    } finally {
      setBusy(false);
    }
  };

  return (
    <Stack spacing={2}>
      <Shell
        title={t("workspaceCv")}
        subtitle={t("assetsCvSubtitle")}
        loading={loading}
        error={error}
      >
        <Stack spacing={2}>
          {(data?.availableVariants.length ?? 0) === 0 ? (
            <Alert severity="info" sx={{ borderRadius: 2 }}>
              {t("assetsNoCvVariants")}
            </Alert>
          ) : (
            <TextField
              select
              size="small"
              fullWidth
              label={t("assetsAttachedCv")}
              value={attached}
              disabled={busy}
              onChange={(e) => attach(e.target.value === "" ? null : Number(e.target.value))}
              helperText={t("assetsAttachedCvHelp")}
            >
              <MenuItem value="">{t("assetsNone")}</MenuItem>
              {(data?.availableVariants ?? []).map((v) => (
                <MenuItem key={v.id} value={v.id}>
                  {v.name} · {v.themeId} · v{v.version}
                </MenuItem>
              ))}
            </TextField>
          )}

          {data?.attachedVariantId ? (
            <Paper variant="outlined" sx={{ p: 1.5, borderRadius: 2 }}>
              <Stack direction="row" justifyContent="space-between" alignItems="center" flexWrap="wrap" gap={1}>
                <Box>
                  <Typography variant="body2" sx={{ fontWeight: 700 }}>{data.attachedVariantName}</Typography>
                  <Typography variant="caption" color="text.secondary">
                    {t("assetsThemeVersion", { theme: data.attachedThemeId ?? "—", version: data.attachedVersion ?? "—" })}
                    {data.attachedIsPublic ? ` · ${t("assetsPublic")}` : ""}
                  </Typography>
                </Box>
                <Stack direction="row" spacing={1}>
                  <Button
                    size="small"
                    variant="outlined"
                    endIcon={<OpenInNewIcon fontSize="small" />}
                    // The builder's real route. It loads the variant from :id and enforces ownership
                    // server-side, so there is no second CV loading path here.
                    href={`/career/builder/${data.attachedVariantId}`}
                  >
                    {t("assetsEditPreviewExport")}
                  </Button>
                  <Button size="small" variant="contained" disabled={busy} onClick={() => void duplicateForJob()}>
                    {t("assetsDuplicateForJob")}
                  </Button>
                </Stack>
              </Stack>
            </Paper>
          ) : (
            <Typography variant="body2" color="text.secondary">
              {t("assetsNoCvAttached")}
            </Typography>
          )}

          {data?.hasTailoredCvText && (
            <Alert severity="info" sx={{ borderRadius: 2 }}>
              {t("assetsLegacyCvText")}
            </Alert>
          )}
        </Stack>
      </Shell>

      <ApplicationTailoringSection jobId={jobId} />
    </Stack>
  );
}

// ---------- Tailoring ----------

export function ApplicationTailoringSection({ jobId }: { jobId: number }) {
  const { t } = useI18n();
  const { data, error, loading } = useAsset<TailoringPlan>(
    () => applicationAssetsApi.tailoring(jobId),
    [jobId],
  );

  return (
    <Shell
      title={t("assetsTailoring")}
      subtitle={t("assetsTailoringSubtitle")}
      loading={loading}
      error={error}
    >
      <Stack spacing={2}>
        {data && !data.hasCareerProfile && (
          <Alert severity="info" sx={{ borderRadius: 2 }}>
            {t("assetsBuildProfile")}
          </Alert>
        )}
        {data && !data.hasJobDescription && (
          <Alert severity="info" sx={{ borderRadius: 2 }}>
            {t("assetsAddAdvert")}
          </Alert>
        )}

        {(data?.suggestions.length ?? 0) === 0 ? (
          <Typography variant="body2" color="text.secondary">
            {t("assetsNoSuggestions")}
          </Typography>
        ) : (
          (data?.suggestions ?? []).map((s) => (
            <Box key={s.kind}>
              <Typography variant="body2" sx={{ fontWeight: 700 }}>{s.title}</Typography>
              {s.detail && (
                <Typography variant="caption" color="text.secondary">{s.detail}</Typography>
              )}
              <Stack direction="row" flexWrap="wrap" gap={0.5} sx={{ mt: 0.75 }}>
                {s.items.map((item) => (
                  <Chip key={item} size="small" label={item} variant="outlined" />
                ))}
              </Stack>
            </Box>
          ))
        )}
      </Stack>
    </Shell>
  );
}

// ---------- Cover letter ----------

const COVER_LETTER_TEMPLATES = {
  en: `Dear Hiring Manager,

I am writing to apply for the [role] position at [company]. [One sentence on why this company, specifically.]

In my current role I [the most relevant thing you have done, with a concrete outcome]. [A second example that matches what the advert asks for.]

[Why you want this job, in your own words.]

I would welcome the chance to talk it through.

Kind regards,
[Your name]`,
  nb: `Kjære rekrutteringsansvarlig,

Jeg søker med dette på stillingen som [stilling] hos [selskap]. [Én konkret setning om hvorfor akkurat dette selskapet.]

I min nåværende rolle har jeg [det mest relevante du har gjort, med et konkret resultat]. [Et annet eksempel som samsvarer med det stillingsannonsen etterspør.]

[Hvorfor du ønsker denne stillingen, med egne ord.]

Jeg ser frem til muligheten til å utdype dette i et intervju.

Med vennlig hilsen
[Navnet ditt]`,
} as const;

export function ApplicationCoverLetterSection({ jobId, onDirtyChange }: { jobId: number; onDirtyChange?: (dirty: boolean) => void }) {
  const { language, t } = useI18n();
  const { data, error, loading, setData, setError } = useAsset<CoverLetter>(
    () => applicationAssetsApi.coverLetter(jobId),
    [jobId],
  );
  const [draft, setDraft] = useState<string | null>(null);
  const [draftAiAction, setDraftAiAction] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);

  // The textarea is only seeded from the server until the user starts typing, so a reload never
  // clobbers unsaved edits.
  const text = draft ?? data?.text ?? "";
  const dirty = draft !== null && draft !== (data?.text ?? "");

  useEffect(() => {
    onDirtyChange?.(dirty);
    return () => onDirtyChange?.(false);
  }, [dirty, onDirtyChange]);

  const save = async (value: string, source = "manual", aiAction?: string) => {
    setBusy(true);
    try {
      setData(await applicationAssetsApi.saveCoverLetter(jobId, value, source, aiAction));
      setDraft(null);
      setDraftAiAction(null);
      setError(null);
    } catch (err) {
      setError(getApiErrorMessage(err, t("assetsCoverSaveFailed")));
    } finally {
      setBusy(false);
    }
  };

  const restore = async (version: number) => {
    setBusy(true);
    try {
      setData(await applicationAssetsApi.restoreCoverLetter(jobId, version));
      setDraft(null);
      setDraftAiAction(null);
      setError(null);
    } catch (err) {
      setError(getApiErrorMessage(err, t("assetsCoverRestoreFailed")));
    } finally {
      setBusy(false);
    }
  };

  return (
    <Stack spacing={2}>
      <Shell
        title={t("workspaceCoverLetter")}
        subtitle={t("assetsCoverSubtitle")}
        loading={loading}
        error={error}
      >
        <Stack spacing={2}>
          <RichTextField
            minRows={12}
            label={t("jobDetailsCoverLetter")}
            value={text}
            disabled={busy}
            onChange={setDraft}
            placeholder={t("assetsCoverPlaceholder")}
          />

          <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
            <Button variant="contained" disabled={busy || !dirty} onClick={() => save(text, draftAiAction ? "ai" : "manual", draftAiAction ?? undefined)}>
              {t("save")}
            </Button>
            <Button disabled={busy || !dirty} onClick={() => { setDraft(null); setDraftAiAction(null); }}>
              {t("assetsDiscardChanges")}
            </Button>
            <Button
              disabled={busy || text.trim().length > 0}
              onClick={() => setDraft(COVER_LETTER_TEMPLATES[language])}
            >
              {t("assetsStartTemplate")}
            </Button>
            {dirty && (
              <Chip size="small" color="warning" variant="outlined" label={t("assetsUnsavedChanges")} />
            )}
          </Stack>
        </Stack>
      </Shell>

      <CoverLetterAiAssistant
        jobId={jobId}
        currentText={text}
        onApply={(value, aiAction) => { setDraft(value); setDraftAiAction(aiAction); }}
      />

      <Shell title={t("assetsVersionHistory")} loading={loading} error={null}>
        {(data?.versions.length ?? 0) === 0 ? (
          <Typography variant="body2" color="text.secondary">
            {t("assetsNoVersions")}
          </Typography>
        ) : (
          <Stack spacing={0.5}>
            {(data?.versions ?? []).map((v) => (
              <Stack
                key={v.version}
                direction="row"
                alignItems="center"
                justifyContent="space-between"
                sx={{ py: 0.5 }}
              >
                <Box sx={{ minWidth: 0 }}>
                  <Stack direction="row" spacing={0.75} alignItems="center" flexWrap="wrap">
                    <Typography variant="body2" sx={{ fontWeight: 600 }}>v{v.version}</Typography>
                    <Chip size="small" variant="outlined" label={v.aiAction ? `${v.source} · ${v.aiAction}` : v.source} />
                    {v.isCurrent && <Chip size="small" color="primary" variant="outlined" label={t("assetsCurrent")} />}
                  </Stack>
                  <Typography variant="caption" color="text.secondary">
                    {new Date(v.createdAtUtc).toLocaleString(language === "nb" ? "nb-NO" : "en")} · {t("assetsCharacterCount", { count: v.length })}
                  </Typography>
                </Box>
                {!v.isCurrent && (
                  <Tooltip title={t("assetsRestoreVersion")}>
                    <span>
                      <IconButton
                        size="small"
                        disabled={busy}
                        aria-label={t("assetsRestoreVersionNumber", { version: v.version })}
                        onClick={() => restore(v.version)}
                      >
                        <RestoreIcon fontSize="small" />
                      </IconButton>
                    </span>
                  </Tooltip>
                )}
              </Stack>
            ))}
          </Stack>
        )}
      </Shell>
    </Stack>
  );
}

const COVER_LETTER_ACTIONS = [
  { key: "generate", labelKey: "coverActionGenerate" },
  { key: "regenerate", labelKey: "coverActionRegenerate" },
  { key: "improve", labelKey: "coverActionImprove" },
  { key: "shorten", labelKey: "coverActionShorten" },
  { key: "expand", labelKey: "coverActionExpand" },
  { key: "professional", labelKey: "coverActionProfessional" },
  { key: "natural", labelKey: "coverActionNatural" },
  { key: "grammar", labelKey: "coverActionGrammar" },
  { key: "tailor", labelKey: "coverActionTailor" },
] as const;

function CoverLetterAiAssistant({ jobId, currentText, onApply }: { jobId: number; currentText: string; onApply: (text: string, action: string) => void }) {
  const { canUseAi } = useAccountPlan();
  const { language: uiLanguage, t } = useI18n();
  const { data: cv, loading: loadingCv } = useAsset<ApplicationCv>(() => applicationAssetsApi.cv(jobId), [jobId]);
  const [action, setAction] = useState("generate");
  const [mode, setMode] = useState("professional");
  const [language, setLanguage] = useState<"en" | "nb-NO">(() => uiLanguage === "nb" ? "nb-NO" : "en");
  const [instructions, setInstructions] = useState("");
  const [suggestion, setSuggestion] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const generate = async () => {
    setBusy(true);
    setError(null);
    try {
      const result = await aiWorkspaceApi.generate(jobId, {
        module: "cover-letter",
        mode,
        action,
        targetLanguage: language,
        currentText: currentText.trim() || undefined,
        extraContext: instructions.trim() || undefined,
      });
      setSuggestion(result.result.text?.trim() ?? "");
    } catch (err) {
      setError(getApiErrorMessage(err, t("coverAiGenerateFailed")));
    } finally {
      setBusy(false);
    }
  };

  const hasCv = !!cv?.attachedVariantId;
  return (
    <Shell
      title={t("coverAiTitle")}
      subtitle={t("coverAiSubtitle")}
      loading={loadingCv}
      error={null}
    >
      <Stack spacing={2}>
        {!hasCv ? (
          <Alert severity="info">{t("coverAiSelectCv")}</Alert>
        ) : (
          <Alert severity="success" variant="outlined" sx={{ py: 0.5 }}>
            {t("coverAiUsing")} <strong>{cv?.attachedVariantName}</strong>
          </Alert>
        )}
        <Stack direction={{ xs: "column", sm: "row" }} spacing={1.5}>
          <FormControl size="small" sx={{ minWidth: 180 }}>
            <InputLabel>{t("coverAiAction")}</InputLabel>
            <Select label={t("coverAiAction")} value={action} onChange={(event) => setAction(event.target.value)}>
              {COVER_LETTER_ACTIONS.map((item) => <MenuItem key={item.key} value={item.key}>{t(item.labelKey)}</MenuItem>)}
            </Select>
          </FormControl>
          <FormControl size="small" sx={{ minWidth: 160 }}>
            <InputLabel>{t("coverAiTone")}</InputLabel>
            <Select label={t("coverAiTone")} value={mode} onChange={(event) => setMode(event.target.value)}>
              {[
                ["professional", "coverToneProfessional"], ["friendly", "coverToneFriendly"], ["short", "coverToneShort"],
                ["detailed", "coverToneDetailed"], ["modern", "coverToneModern"], ["traditional", "coverToneTraditional"],
              ].map(([value, key]) => <MenuItem key={value} value={value}>{t(key as any)}</MenuItem>)}
            </Select>
          </FormControl>
          <FormControl size="small" sx={{ minWidth: 190 }}>
            <InputLabel>{t("coverAiDocumentLanguage")}</InputLabel>
            <Select label={t("coverAiDocumentLanguage")} value={language} onChange={(event) => setLanguage(event.target.value as "en" | "nb-NO")}>
              <MenuItem value="en">{t("languageEnglish")}</MenuItem>
              <MenuItem value="nb-NO">{t("languageNorwegianBokmal")}</MenuItem>
            </Select>
          </FormControl>
        </Stack>
        <TextField
          label={t("coverAiAdditionalInstructions")}
          placeholder={t("coverAiInstructionsPlaceholder")}
          value={instructions}
          onChange={(event) => setInstructions(event.target.value)}
          multiline
          minRows={2}
          fullWidth
        />
        {error && <Alert severity="error" action={<Button color="inherit" size="small" onClick={() => void generate()}>{t("retry")}</Button>}>{error}</Alert>}
        <Button
          variant="contained"
          startIcon={<AutoFixHighIcon />}
          disabled={!canUseAi || !hasCv || busy}
          onClick={() => void generate()}
          sx={{ alignSelf: "flex-start" }}
        >
          {busy ? t("interviewAiGenerating") : canUseAi ? t(COVER_LETTER_ACTIONS.find((item) => item.key === action)?.labelKey ?? "coverActionGenerate") : t("interviewAiProRequired")}
        </Button>

        {suggestion && (
          <Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", lg: "1fr 1fr" }, gap: 1.5 }}>
            <Paper variant="outlined" sx={{ p: 2, minWidth: 0 }}>
              <Typography variant="overline" color="text.secondary">{t("coverAiCurrent")}</Typography>
              <Typography variant="body2" sx={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}>
                {currentText || t("coverAiNoDraft")}
              </Typography>
            </Paper>
            <Paper variant="outlined" sx={{ p: 2, minWidth: 0, borderColor: "primary.main" }}>
              <Typography variant="overline" color="primary">{t("coverAiSuggestion")}</Typography>
              <Typography variant="body2" sx={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}>{suggestion}</Typography>
              <Stack direction="row" spacing={1} sx={{ mt: 2 }}>
                <Button size="small" variant="contained" onClick={() => { onApply(suggestion, action); setSuggestion(""); }}>{t("coverAiApply")}</Button>
                <Button size="small" onClick={() => setSuggestion("")}>{t("coverAiReject")}</Button>
              </Stack>
            </Paper>
          </Box>
        )}
      </Stack>
    </Shell>
  );
}

// ---------- Application answer and recruiter message ----------

type PackageDrafts = { applicationAnswer: string; recruiterMessage: string };

export function ApplicationPackageDraftsSection({
  jobId,
  initialApplicationAnswer,
  initialRecruiterMessage,
  onSaved,
  onDirtyChange,
}: {
  jobId: number;
  initialApplicationAnswer: string;
  initialRecruiterMessage: string;
  onSaved?: () => void;
  onDirtyChange?: (dirty: boolean) => void;
}) {
  const { t } = useI18n();
  const initial = { applicationAnswer: initialApplicationAnswer, recruiterMessage: initialRecruiterMessage };
  const [saved, setSaved] = useState<PackageDrafts>(initial);
  const [draft, setDraft] = useState<PackageDrafts | null>(null);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (draft === null) setSaved(initial);
    // `draft` is deliberately excluded: a parent refresh must never overwrite in-progress edits.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [jobId, initialApplicationAnswer, initialRecruiterMessage]);

  const value = draft ?? saved;
  const dirty = draft !== null && (
    draft.applicationAnswer !== saved.applicationAnswer ||
    draft.recruiterMessage !== saved.recruiterMessage
  );

  useEffect(() => {
    onDirtyChange?.(dirty);
    return () => onDirtyChange?.(false);
  }, [dirty, onDirtyChange]);
  const update = (patch: Partial<PackageDrafts>) => setDraft({ ...value, ...patch });

  const save = async () => {
    setBusy(true);
    try {
      await applicationAssetsApi.saveApplicationDrafts(jobId, value.applicationAnswer, value.recruiterMessage);
      setSaved(value);
      setDraft(null);
      setError(null);
      onSaved?.();
    } catch (err) {
      setError(getApiErrorMessage(err, t("assetsDraftsSaveFailed")));
    } finally {
      setBusy(false);
    }
  };

  return (
    <Shell
      title={t("assetsApplicationDrafts")}
      subtitle={t("assetsApplicationDraftsSubtitle")}
      loading={false}
      error={error}
    >
      <Stack spacing={2}>
        <RichTextField
          minRows={6}
          label={t("assetsApplicationAnswer")}
          value={value.applicationAnswer}
          disabled={busy}
          onChange={(applicationAnswer) => update({ applicationAnswer })}
          placeholder={t("assetsApplicationAnswerPlaceholder")}
        />
        <RichTextField
          minRows={4}
          label={t("assetsRecruiterMessage")}
          value={value.recruiterMessage}
          disabled={busy}
          onChange={(recruiterMessage) => update({ recruiterMessage })}
          placeholder={t("assetsRecruiterMessagePlaceholder")}
        />
        <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap alignItems="center">
          <Button variant="contained" disabled={busy || !dirty} onClick={save}>
            {t("assetsSaveDrafts")}
          </Button>
          <Button disabled={busy || !dirty} onClick={() => setDraft(null)}>
            {t("assetsDiscardChanges")}
          </Button>
          {dirty && <Chip size="small" color="warning" variant="outlined" label={t("assetsUnsavedChanges")} />}
        </Stack>
      </Stack>
    </Shell>
  );
}
