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

import {
  Alert,
  Box,
  Button,
  Chip,
  CircularProgress,
  Dialog,
  DialogContent,
  DialogTitle,
  FormControl,
  InputLabel,
  MenuItem,
  Select,
  Tab,
  Tabs,
  TextField,
  Typography,
} from "@mui/material";
import { alpha } from "@mui/material/styles";

import { api, getApiErrorMessage } from "../api";
import { ApplicationPackageResponse, CandidateFit, FocusPlanResponse, FollowUpDraft, InterviewPrepResponse, JobApplication, MatchScore, ReadinessResponse, StatusSuggestion, StrategySnapshotOperationResponse, TailoredCvDraft, UserOperation } from "../types";
import { statusLabel } from "../pipeline";
import { useToast } from "../toast";
import { useDialogActions } from "../dialogs";
import { emptyTailoredCvDraft, joinLines, normalizeTailoredCvDraft, splitLines } from "../tailoredCvDraft";

import Correspondence from "./Correspondence";
import Attachments from "./Attachments";
import AiWorkspacePanel from "./AiWorkspacePanel";
import JobInsightTabs from "./JobInsightTabs";
import { DraftCard, ListCard, MatchScoreCard, PaperRow, SectionChips, TwoColumnSection, WorkspaceDraftCard } from "./JobDetailsPanels";
import JobFlowBar from "./JobFlowBar";
import GradientButton from "./GradientButton";
import { useI18n } from "../i18n/I18nProvider";
import { useJobWorkspaceBaseData } from "./job-workspace/useJobWorkspaceBaseData";
import { useWorkspaceTabCache } from "./job-workspace/useWorkspaceTabCache";
import { useAccountPlan } from "../accountPlan";

type GenerationMode = "default" | "concise" | "ats" | "achievement" | "interview";
type CoverLetterStyle = "balanced" | "concise" | "formal" | "bold";

type TailoredCvPreviewResponse = {
  templateId: string;
  html: string;
  suggestedFileName: string;
};

interface Props {
  open: boolean;
  jobId: number | null;
  onClose: () => void;
  initialTab?: number;
  initialFollowUpMode?: string;
  // Supplied by callers that live inside the router. Optional on purpose: the dialog must not depend
  // on router context, so it stays renderable standalone (and in tests) without a <Router>.
  onOpenWorkspace?: (jobId: number) => void;
}

function statusChipColor(status: string): "default" | "primary" | "warning" | "error" | "success" {
  switch (status) {
    case "Rejected":
      return "error";
    case "Waiting":
    case "Ghosted":
      return "warning";
    case "Offer":
      return "success";
    case "Applied":
    default:
      return "primary";
  }
}

function getFitLevel(candidateFit: CandidateFit | null): { label: string; color: "success" | "warning" | "default" } | null {
  if (!candidateFit) return null;
  if (candidateFit.fitLevel === "Strong match") return { label: candidateFit.fitLevel, color: "success" };
  if (candidateFit.fitLevel === "Potential match") return { label: candidateFit.fitLevel, color: "warning" };
  return { label: candidateFit.fitLevel, color: "default" };
}

function copyLines(items: string[]) {
  return navigator.clipboard.writeText(items.map((item) => `• ${item}`).join("\n"));
}

const APPLICATION_ANSWER_START = "<<<APPLICATION_ANSWER_DRAFT>>>";
const APPLICATION_ANSWER_END = "<<<END_APPLICATION_ANSWER_DRAFT>>>";

function upsertApplicationAnswerDraft(notes: string | null | undefined, draft: string) {
  const trimmedNotes = (notes ?? "").trim();
  const trimmedDraft = draft.trim();
  const block = trimmedDraft
    ? `${APPLICATION_ANSWER_START}\n${trimmedDraft}\n${APPLICATION_ANSWER_END}`
    : "";

  if (!trimmedNotes) return block;

  const markerPattern = new RegExp(`${APPLICATION_ANSWER_START}[\\s\\S]*?${APPLICATION_ANSWER_END}`, "g");
  if (markerPattern.test(trimmedNotes)) {
    return block ? trimmedNotes.replace(markerPattern, block).trim() : trimmedNotes.replace(markerPattern, "").trim();
  }

  const legacyPattern = /(?:\n\n)?Application answer draft:\s*\n[\s\S]*$/i;
  if (legacyPattern.test(trimmedNotes)) {
    return block ? trimmedNotes.replace(legacyPattern, `\n\n${block}`).trim() : trimmedNotes.replace(legacyPattern, "").trim();
  }

  return block ? `${trimmedNotes}\n\n${block}` : trimmedNotes;
}

function getWorkspaceStatus(currentValue: string, savedValue: string) {
  const current = currentValue.trim();
  const saved = savedValue.trim();
  if (current && current !== saved) return { label: "Unsaved edits", color: "warning" as const };
  if (saved) return { label: "Saved to job", color: "success" as const };
  if (current) return { label: "Generated only", color: "default" as const };
  return { label: "Empty", color: "default" as const };
}

function serializeTailoredDraft(draft: TailoredCvDraft) {
  const normalized = normalizeTailoredCvDraft(draft);
  return JSON.stringify({
    templateId: normalized.templateId,
    headline: normalized.headline ?? "",
    summary: normalized.summary,
    selectedSkills: normalized.selectedSkills,
    experience: normalized.experience,
    education: normalized.education,
    customSections: normalized.customSections,
    renderOptions: normalized.renderOptions,
    status: normalized.status,
    isLegacyFallback: normalized.isLegacyFallback,
  });
}

export default function JobDetailsDialog({ open, jobId, onClose, initialTab = 0, initialFollowUpMode, onOpenWorkspace }: Props) {
  const { canUseAi } = useAccountPlan();
  const { toast } = useToast();
  const { t } = useI18n();
  const { confirmAction } = useDialogActions();
  const followUpCache = useWorkspaceTabCache<FollowUpDraft | null>();
  const candidateFitCache = useWorkspaceTabCache<CandidateFit | null>();
  const matchScoreCache = useWorkspaceTabCache<MatchScore | null>();
  const focusPlanCache = useWorkspaceTabCache<FocusPlanResponse | null>();
  const interviewPrepCache = useWorkspaceTabCache<InterviewPrepResponse | null>();
  const readinessCache = useWorkspaceTabCache<ReadinessResponse | null>();
  const tailoredDraftCache = useWorkspaceTabCache<TailoredCvDraft>();

  const {
    job,
    setJob,
    tab,
    setTab,
    history,
    isAdmin,
    jobAttachments,
    selectedAttachmentIds,
    setSelectedAttachmentIds,
    profileAvatarImageDataUrl,
    packageWorkspace,
    setPackageWorkspace,
    savedPackageWorkspace,
    setSavedPackageWorkspace,
    packageGeneratedAt,
    setPackageGeneratedAt,
    draftRecipient,
    setDraftRecipient,
    followUpMode,
    setFollowUpMode,
  } = useJobWorkspaceBaseData({
    open,
    jobId,
    initialTab,
    initialFollowUpMode,
  });

  const [followUpDraft, setFollowUpDraft] = useState<FollowUpDraft | null>(null);
  const [loadingDraft, setLoadingDraft] = useState(false);
  const [refreshingAi, setRefreshingAi] = useState(false);
  const [candidateFit, setCandidateFit] = useState<CandidateFit | null>(null);
  const [matchScore, setMatchScore] = useState<MatchScore | null>(null);
  const [loadingMatchScore, setLoadingMatchScore] = useState(false);
  const [statusSuggestion, setStatusSuggestion] = useState<StatusSuggestion | null>(null);
  const [applyingStatusSuggestion, setApplyingStatusSuggestion] = useState(false);
  const [focusPlan, setFocusPlan] = useState<FocusPlanResponse | null>(null);
  const [loadingCandidateFit, setLoadingCandidateFit] = useState(false);
  const [loadingFocusPlan, setLoadingFocusPlan] = useState(false);
  const [focusPlanOperation, setFocusPlanOperation] = useState<UserOperation | null>(null);
  const announcedFocusPlanOperation = useRef<string | null>(null);
  const focusPlanLookupVersion = useRef(0);
  const [loadingStrategySnapshot, setLoadingStrategySnapshot] = useState(false);
  const [interviewPrep, setInterviewPrep] = useState<InterviewPrepResponse | null>(null);
  const [loadingInterviewPrep, setLoadingInterviewPrep] = useState(false);
  const [readiness, setReadiness] = useState<ReadinessResponse | null>(null);
  const [loadingReadiness, setLoadingReadiness] = useState(false);
  const [savingApplicationDrafts, setSavingApplicationDrafts] = useState(false);
  const [generatingPackage, setGeneratingPackage] = useState(false);
  const [applicationPackage, setApplicationPackage] = useState<ApplicationPackageResponse | null>(null);
  const [generationMode, setGenerationMode] = useState<GenerationMode>("default");
  const [coverLetterStyle, setCoverLetterStyle] = useState<CoverLetterStyle>("balanced");
  const [tailoredCvDraft, setTailoredCvDraft] = useState<TailoredCvDraft>(emptyTailoredCvDraft());
  const [savedTailoredCvDraft, setSavedTailoredCvDraft] = useState<TailoredCvDraft>(emptyTailoredCvDraft());
  const [loadingTailoredCvDraft, setLoadingTailoredCvDraft] = useState(false);
  const [generatingTailoredCvDraft, setGeneratingTailoredCvDraft] = useState(false);
  const [savingTailoredCvDraft, setSavingTailoredCvDraft] = useState(false);
  const [tailoredCvPreview, setTailoredCvPreview] = useState<TailoredCvPreviewResponse | null>(null);
  const [loadingTailoredCvPreview, setLoadingTailoredCvPreview] = useState(false);
  const [exportingTailoredCvPdf, setExportingTailoredCvPdf] = useState(false);
  const [customPhotoDataUrl, setCustomPhotoDataUrl] = useState<string | null>(null);
  const [useProfilePhoto, setUseProfilePhoto] = useState(true);
  const [draftReloadToken, setDraftReloadToken] = useState(0);
  const [draftSubject, setDraftSubject] = useState("");
  const [draftBody, setDraftBody] = useState("");
  const selectedAttachmentCsv = useMemo(() => selectedAttachmentIds.join(","), [selectedAttachmentIds]);

  useEffect(() => {
    if (!open || !jobId) return;
    setFollowUpDraft(null);
    setCandidateFit(null);
    setMatchScore(null);
    setStatusSuggestion(null);
    setFocusPlan(null);
    setInterviewPrep(null);
    setReadiness(null);
    setApplicationPackage(null);
    setTailoredCvDraft(emptyTailoredCvDraft());
    setSavedTailoredCvDraft(emptyTailoredCvDraft());
    setTailoredCvPreview(null);
    setCustomPhotoDataUrl(null);
    setUseProfilePhoto(true);
    setDraftReloadToken(0);
    setDraftSubject("");
    setDraftBody("");
    setFocusPlanOperation(null);
    announcedFocusPlanOperation.current = null;
    focusPlanLookupVersion.current += 1;
    followUpCache.clearCached();
    candidateFitCache.clearCached();
    focusPlanCache.clearCached();
    interviewPrepCache.clearCached();
    readinessCache.clearCached();
    tailoredDraftCache.clearCached();
  }, [open, jobId, followUpCache, candidateFitCache, focusPlanCache, interviewPrepCache, readinessCache, tailoredDraftCache]);

  useEffect(() => {
    if (!open || !jobId || tab !== 3) return;
    const cacheKey = `${jobId}:tailored-cv-draft`;
    const cached = tailoredDraftCache.getCached(cacheKey);
    if (cached) {
      const normalized = normalizeTailoredCvDraft(cached);
      setTailoredCvDraft(normalized);
      setSavedTailoredCvDraft(normalized);
      return;
    }

    setLoadingTailoredCvDraft(true);
    api.get<TailoredCvDraft>(`/jobapplications/${jobId}/tailored-cv-draft`).then((r) => {
      const normalized = normalizeTailoredCvDraft(r.data);
      tailoredDraftCache.setCached(cacheKey, normalized);
      setTailoredCvDraft(normalized);
      setSavedTailoredCvDraft(normalized);
    }).catch(() => {
      const empty = emptyTailoredCvDraft();
      setTailoredCvDraft(empty);
      setSavedTailoredCvDraft(empty);
    }).finally(() => setLoadingTailoredCvDraft(false));
  }, [open, jobId, tab, tailoredDraftCache]);

  useEffect(() => {
    if (!canUseAi || !open || !jobId || tab !== 4) return;
    const cacheKey = `${jobId}:followup:${followUpMode}:${selectedAttachmentCsv || "none"}:${draftReloadToken}`;
    const cached = followUpCache.getCached(cacheKey);
    if (cached) {
      setFollowUpDraft(cached);
      setDraftSubject(cached.subject);
      setDraftBody(cached.body);
      return;
    }

    setLoadingDraft(true);
    api.get<FollowUpDraft>(`/jobapplications/${jobId}/followup-draft`, { params: { mode: followUpMode, attachmentIds: selectedAttachmentCsv || undefined } }).then((r) => {
      followUpCache.setCached(cacheKey, r.data);
      setFollowUpDraft(r.data);
      setDraftSubject(r.data.subject);
      setDraftBody(r.data.body);
    }).catch(() => setFollowUpDraft(null)).finally(() => setLoadingDraft(false));
  }, [canUseAi, open, jobId, tab, followUpMode, draftReloadToken, selectedAttachmentCsv, followUpCache]);

  useEffect(() => {
    if (!canUseAi || !open || !jobId || tab !== 5 || candidateFit) return;
    const cacheKey = `${jobId}:candidate-fit:${selectedAttachmentCsv || "none"}`;
    const cached = candidateFitCache.getCached(cacheKey);
    if (cached) {
      setCandidateFit(cached);
      return;
    }

    setLoadingCandidateFit(true);
    api.get<CandidateFit>(`/jobapplications/${jobId}/candidate-fit`, { params: { attachmentIds: selectedAttachmentCsv || undefined } }).then((r) => {
      candidateFitCache.setCached(cacheKey, r.data);
      setCandidateFit(r.data);
    }).catch(() => setCandidateFit(null)).finally(() => setLoadingCandidateFit(false));
  }, [canUseAi, open, jobId, tab, candidateFit, selectedAttachmentCsv, candidateFitCache]);

  // Persisted server-side like interview prep (career-workspace-implementation-roadmap.md Phase
  // F5); Regenerate is the explicit escape hatch when the job has changed since it was written.
  const regenerateCandidateFit = useCallback(() => {
    if (!canUseAi || !jobId) return;
    setLoadingCandidateFit(true);
    api.get<CandidateFit>(`/jobapplications/${jobId}/candidate-fit`, { params: { attachmentIds: selectedAttachmentCsv || undefined, refresh: true } }).then((r) => {
      candidateFitCache.setCached(`${jobId}:candidate-fit:${selectedAttachmentCsv || "none"}`, r.data);
      setCandidateFit(r.data);
      toast("Candidate fit regenerated.", "success");
    }).catch((error: any) => toast(getApiErrorMessage(error, "Failed to regenerate candidate fit."), "error")).finally(() => setLoadingCandidateFit(false));
  }, [canUseAi, jobId, selectedAttachmentCsv, candidateFitCache, toast]);

  // Match score is deterministic and cheap: load it on the Candidate Fit tab
  // independently of the slow AI narrative so users see the number instantly.
  useEffect(() => {
    if (!open || !jobId || tab !== 5 || matchScore) return;
    const cacheKey = `${jobId}:match-score`;
    const cached = matchScoreCache.getCached(cacheKey);
    if (cached) {
      setMatchScore(cached);
      return;
    }

    setLoadingMatchScore(true);
    api.get<MatchScore>(`/jobapplications/${jobId}/match-score`).then((r) => {
      matchScoreCache.setCached(cacheKey, r.data);
      setMatchScore(r.data);
    }).catch(() => setMatchScore(null)).finally(() => setLoadingMatchScore(false));
  }, [open, jobId, tab, matchScore, matchScoreCache]);

  const updateLearningRecommendation = useCallback(async (id: number, status: "done" | "dismissed") => {
    if (!jobId) return;
    try {
      await api.patch(`/jobapplications/${jobId}/checklist/${id}`, { status });
      setMatchScore(current => {
        if (!current) return current;
        const updated = {
          ...current,
          learningRecommendations: (current.learningRecommendations ?? []).map(item => item.id === id ? { ...item, status } : item),
        };
        matchScoreCache.setCached(`${jobId}:match-score`, updated);
        return updated;
      });
    } catch (error: any) {
      toast(getApiErrorMessage(error, t("matchScoreLearningUpdateFailed")), "error");
    }
  }, [jobId, matchScoreCache, t, toast]);

  // Suggest a status move from the latest inbound email when the workspace opens.
  useEffect(() => {
    if (!open || !jobId) return;
    let cancelled = false;
    api.get<StatusSuggestion>(`/jobapplications/${jobId}/status-suggestion`)
      .then((r) => { if (!cancelled) setStatusSuggestion(r.data?.hasSuggestion ? r.data : null); })
      .catch(() => { if (!cancelled) setStatusSuggestion(null); });
    return () => { cancelled = true; };
  }, [open, jobId]);

  const applyStatusSuggestion = async () => {
    if (!jobId || !statusSuggestion?.suggestedStatus) return;
    setApplyingStatusSuggestion(true);
    try {
      await api.patch(`/jobapplications/${jobId}/status`, { status: statusSuggestion.suggestedStatus });
      setJob((prev) => prev ? { ...prev, status: statusSuggestion.suggestedStatus! } : prev);
      setStatusSuggestion(null);
      toast(t("statusSuggestionApplied"), "success");
    } catch (error: any) {
      toast(getApiErrorMessage(error, t("statusSuggestionFailed")), "error");
    } finally {
      setApplyingStatusSuggestion(false);
    }
  };

  const loadCachedFocusPlan = useCallback(async () => {
    if (!jobId) return null;
    const cacheKey = `${jobId}:focus-plan:${selectedAttachmentCsv || "none"}`;
    const cached = focusPlanCache.getCached(cacheKey);
    if (cached) {
      setFocusPlan(cached);
      return cached;
    }
    try {
      const r = await api.get<FocusPlanResponse>(`/jobapplications/${jobId}/focus-plan`, { params: { attachmentIds: selectedAttachmentCsv || undefined } });
      focusPlanCache.setCached(cacheKey, r.data);
      setFocusPlan(r.data);
      return r.data;
    } catch {
      setFocusPlan(null);
      return null;
    }
  }, [jobId, selectedAttachmentCsv, focusPlanCache]);

  useEffect(() => {
    if (!canUseAi || !open || !jobId || tab !== 6 || focusPlan) return;
    setLoadingFocusPlan(true);
    void loadCachedFocusPlan().finally(() => setLoadingFocusPlan(false));
  }, [canUseAi, open, jobId, tab, focusPlan, loadCachedFocusPlan]);

  useEffect(() => {
    if (!canUseAi || !open || !jobId) return;
    const version = ++focusPlanLookupVersion.current;
    api.get<UserOperation>(`/jobapplications/${jobId}/focus-plan/operation`, { params: { attachmentIds: selectedAttachmentCsv || undefined } })
      .then(response => { if (focusPlanLookupVersion.current === version) setFocusPlanOperation(response.data); })
      .catch(() => { if (focusPlanLookupVersion.current === version) setFocusPlanOperation(null); });
  }, [canUseAi, open, jobId, selectedAttachmentCsv]);

  useEffect(() => {
    if (!open || !focusPlanOperation || ["succeeded", "failed", "cancelled"].includes(focusPlanOperation.status)) return;
    const timer = window.setTimeout(() => {
      api.get<UserOperation>(`/operations/${focusPlanOperation.id}`)
        .then(response => setFocusPlanOperation(response.data))
        .catch(() => undefined);
    }, 1000);
    return () => window.clearTimeout(timer);
  }, [open, focusPlanOperation]);

  useEffect(() => {
    if (!focusPlanOperation || !["succeeded", "failed", "cancelled"].includes(focusPlanOperation.status) || announcedFocusPlanOperation.current === `${focusPlanOperation.id}:${focusPlanOperation.status}`) return;
    announcedFocusPlanOperation.current = `${focusPlanOperation.id}:${focusPlanOperation.status}`;
    if (focusPlanOperation.status === "succeeded") {
      void loadCachedFocusPlan().then(() => toast("Strategy snapshot completed.", "success"));
    } else if (focusPlanOperation.status === "failed") {
      toast("Strategy snapshot failed. You can retry safely.", "error");
    } else {
      toast("Strategy snapshot cancelled.", "info");
    }
  }, [focusPlanOperation, loadCachedFocusPlan, toast]);

  const regenerateFocusPlan = useCallback(async () => {
    if (!canUseAi || !jobId) return;
    setLoadingFocusPlan(true);
    try {
      const response = await api.post<StrategySnapshotOperationResponse>(`/jobapplications/${jobId}/focus-plan/operations`, { attachmentIds: selectedAttachmentCsv || null });
      focusPlanLookupVersion.current += 1;
      announcedFocusPlanOperation.current = null;
      setFocusPlanOperation(response.data.operation);
      toast(response.data.created ? "Strategy snapshot queued." : "Strategy snapshot is already queued.", "info");
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to queue strategy snapshot."), "error");
    } finally {
      setLoadingFocusPlan(false);
    }
  }, [canUseAi, jobId, selectedAttachmentCsv, toast]);

  const cancelFocusPlan = useCallback(async () => {
    if (!focusPlanOperation?.canCancel) return;
    try {
      const response = await api.post<UserOperation>(`/operations/${focusPlanOperation.id}/cancel`);
      setFocusPlanOperation(response.data);
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to cancel strategy snapshot."), "error");
    }
  }, [focusPlanOperation, toast]);

  const retryFocusPlan = useCallback(async () => {
    if (!focusPlanOperation?.canRetry) return;
    try {
      announcedFocusPlanOperation.current = null;
      const response = await api.post<UserOperation>(`/operations/${focusPlanOperation.id}/retry`);
      setFocusPlanOperation(response.data);
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to retry strategy snapshot."), "error");
    }
  }, [focusPlanOperation, toast]);

  useEffect(() => {
    if (!canUseAi || !open || !jobId || tab !== 7 || interviewPrep) return;
    const cacheKey = `${jobId}:interview-prep:${selectedAttachmentCsv || "none"}`;
    const cached = interviewPrepCache.getCached(cacheKey);
    if (cached) {
      setInterviewPrep(cached);
      return;
    }

    setLoadingInterviewPrep(true);
    api.get<InterviewPrepResponse>(`/jobapplications/${jobId}/interview-prep/brief`, { params: { attachmentIds: selectedAttachmentCsv || undefined } }).then((r) => {
      interviewPrepCache.setCached(cacheKey, r.data);
      setInterviewPrep(r.data);
    }).catch(() => setInterviewPrep(null)).finally(() => setLoadingInterviewPrep(false));
  }, [canUseAi, open, jobId, tab, interviewPrep, selectedAttachmentCsv, interviewPrepCache]);

  // Interview prep is now persisted server-side (career-workspace-implementation-roadmap.md
  // Phase F5) so it survives tab switches without re-running the AI call. Regenerate is the
  // explicit escape hatch for when the underlying job/notes have changed since it was written.
  const regenerateInterviewPrep = useCallback(() => {
    if (!canUseAi || !jobId) return;
    setLoadingInterviewPrep(true);
    api.get<InterviewPrepResponse>(`/jobapplications/${jobId}/interview-prep/brief`, { params: { attachmentIds: selectedAttachmentCsv || undefined, refresh: true } }).then((r) => {
      interviewPrepCache.setCached(`${jobId}:interview-prep:${selectedAttachmentCsv || "none"}`, r.data);
      setInterviewPrep(r.data);
      toast("Interview prep regenerated.", "success");
    }).catch((error: any) => toast(getApiErrorMessage(error, "Failed to regenerate interview prep."), "error")).finally(() => setLoadingInterviewPrep(false));
  }, [canUseAi, jobId, selectedAttachmentCsv, interviewPrepCache, toast]);

  useEffect(() => {
    setFollowUpDraft(null);
    setCandidateFit(null);
    setFocusPlan(null);
    setInterviewPrep(null);
  }, [selectedAttachmentCsv]);

  useEffect(() => {
    if (!open || !jobId || tab !== 8 || readiness) return;
    const cacheKey = `${jobId}:readiness`;
    const cached = readinessCache.getCached(cacheKey);
    if (cached) {
      setReadiness(cached);
      return;
    }

    setLoadingReadiness(true);
    api.get<ReadinessResponse>(`/jobapplications/${jobId}/readiness`).then((r) => {
      readinessCache.setCached(cacheKey, r.data);
      setReadiness(r.data);
    }).catch(() => setReadiness(null)).finally(() => setLoadingReadiness(false));
  }, [open, jobId, tab, readiness, readinessCache]);

  const tags: string[] = (() => {
    const raw = job?.tags;
    if (!raw) return [];
    try {
      const parsed = JSON.parse(raw);
      return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : [];
    } catch {
      return [];
    }
  })();

  const title = job ? `${job.company?.name ?? ""} - ${job.jobTitle}` : t("addJobApplication");
  const checklist = [
    job?.hasResume ? t("jobDetailsResume") : null,
    job?.hasCoverLetter ? t("jobDetailsCoverLetter") : null,
    job?.hasPortfolio ? t("jobDetailsPortfolio") : null,
    job?.hasOtherAttachment ? t("jobDetailsOther") : null,
  ].filter(Boolean).join(", ") || t("jobDetailsNotAvailable");
  const summaryFirstText = job?.fullSummary ?? job?.shortSummary ?? t("jobTableNoSummaryYet");
  const translatedDescriptionText = job?.translatedDescription?.trim() || "";
  const originalDescriptionText = job?.description?.trim() || "";
  const showTranslatedText = translatedDescriptionText.length > 0;
  const showOriginalText = originalDescriptionText.length > 0;
  const fitLevel = useMemo(() => getFitLevel(candidateFit), [candidateFit]);
  const showAiAttachmentPicker = tab >= 3 && tab <= 7 && jobAttachments.length > 0;

  const attachmentPicker = showAiAttachmentPicker ? (
    <Box sx={{ mb: 2, p: 1.5, borderRadius: 3, boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", backgroundColor: "background.default" }}>
      <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap", mb: 1 }}>
        <Typography variant="caption" sx={{ color: "text.secondary" }}>{t("jobDetailsAttachmentContextPicker")}</Typography>
        <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap" }}>
          <Button size="small" variant="text" onClick={() => setSelectedAttachmentIds(jobAttachments.slice(0, 4).map((item) => item.id))}>{t("jobDetailsAttachmentSelectTop")}</Button>
          <Button size="small" variant="text" onClick={() => setSelectedAttachmentIds([])}>{t("jobDetailsAttachmentClear")}</Button>
        </Box>
      </Box>
      <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap" }}>
        {jobAttachments.map((attachment) => {
          const selected = selectedAttachmentIds.includes(attachment.id);
          return (
            <Chip
              key={attachment.id}
              label={attachment.fileName}
              color={selected ? "primary" : "default"}
              variant={selected ? "filled" : "outlined"}
              onClick={() => setSelectedAttachmentIds((current) => current.includes(attachment.id) ? current.filter((id) => id !== attachment.id) : [...current, attachment.id].slice(-4))}
            />
          );
        })}
      </Box>
    </Box>
  ) : null;

  const tailoredCvDraftStatus = getWorkspaceStatus(tailoredCvDraft.renderedText, savedTailoredCvDraft.renderedText);
  const coverLetterStatus = getWorkspaceStatus(packageWorkspace.coverLetter, savedPackageWorkspace.coverLetter);
  const applicationAnswerStatus = getWorkspaceStatus(packageWorkspace.applicationAnswer, savedPackageWorkspace.applicationAnswer);
  const recruiterMessageStatus = getWorkspaceStatus(packageWorkspace.recruiterMessage, savedPackageWorkspace.recruiterMessage);
  const hasUnsavedTailoredCvDraftChanges = serializeTailoredDraft(tailoredCvDraft) !== serializeTailoredDraft(savedTailoredCvDraft);
  const hasUnsavedPackageChanges = [
    packageWorkspace.coverLetter.trim() !== savedPackageWorkspace.coverLetter.trim(),
    packageWorkspace.applicationAnswer.trim() !== savedPackageWorkspace.applicationAnswer.trim(),
    packageWorkspace.recruiterMessage.trim() !== savedPackageWorkspace.recruiterMessage.trim(),
  ].some(Boolean);

  const saveTailoredCvDraft = async () => {
    if (!jobId) return;

    const normalized = normalizeTailoredCvDraft({
      ...tailoredCvDraft,
      status: tailoredCvDraft.status === "empty" ? "edited" : tailoredCvDraft.status,
    });

    try {
      setSavingTailoredCvDraft(true);
      await api.put(`/jobapplications/${jobId}/tailored-cv-draft`, {
        templateId: normalized.templateId,
        headline: normalized.headline,
        summary: normalized.summary,
        selectedSkills: normalized.selectedSkills,
        experience: normalized.experience,
        education: normalized.education,
        customSections: normalized.customSections,
        renderOptions: normalized.renderOptions,
        status: normalized.status,
      });
      tailoredDraftCache.setCached(`${jobId}:tailored-cv-draft`, normalized);
      setTailoredCvDraft(normalized);
      setSavedTailoredCvDraft(normalized);
      setJob((prev) => prev ? {
        ...prev,
        tailoredCvText: normalized.renderedText,
        tailoredCvUpdatedAt: new Date().toISOString(),
      } : prev);
      readinessCache.clearCached();
      setReadiness(null);
      toast("Tailored CV draft saved.", "success");
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to save the tailored CV draft."), "error");
    } finally {
      setSavingTailoredCvDraft(false);
    }
  };

  const generateTailoredCvDraft = async () => {
    if (!jobId) return;
    if (hasUnsavedTailoredCvDraftChanges) {
      const confirmed = await confirmAction("Regenerating the tailored CV draft will replace your unsaved edits.", {
        title: "Replace unsaved tailored CV edits?",
        confirmLabel: "Regenerate draft",
      });
      if (!confirmed) return;
    }

    try {
      setGeneratingTailoredCvDraft(true);
      const res = await api.post<TailoredCvDraft>(`/jobapplications/${jobId}/generate-tailored-cv-draft`, null, { params: { mode: generationMode } });
      const normalized = normalizeTailoredCvDraft(res.data);
      tailoredDraftCache.setCached(`${jobId}:tailored-cv-draft`, normalized);
      setTailoredCvDraft(normalized);
      setSavedTailoredCvDraft(normalized);
      setJob((prev) => prev ? {
        ...prev,
        tailoredCvText: normalized.renderedText,
        tailoredCvUpdatedAt: new Date().toISOString(),
      } : prev);
      toast("Tailored CV draft generated.", "success");
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to generate a tailored CV draft."), "error");
    } finally {
      setGeneratingTailoredCvDraft(false);
    }
  };

  const resetTailoredCvDraftToSaved = () => {
    setTailoredCvDraft(savedTailoredCvDraft);
    toast("Restored the last saved tailored CV draft.", "info");
  };

  const buildTailoredCvRenderPayload = () => ({
    templateId: tailoredCvDraft.templateId,
    headline: tailoredCvDraft.headline,
    summary: tailoredCvDraft.summary,
    selectedSkills: tailoredCvDraft.selectedSkills,
    experience: tailoredCvDraft.experience,
    education: tailoredCvDraft.education,
    customSections: tailoredCvDraft.customSections,
    renderOptions: tailoredCvDraft.renderOptions,
    photoDataUrl: customPhotoDataUrl,
    useProfileAvatar: useProfilePhoto,
  });

  const refreshTailoredCvPreview = async () => {
    if (!jobId) return;
    try {
      setLoadingTailoredCvPreview(true);
      const res = await api.post<TailoredCvPreviewResponse>(`/jobapplications/${jobId}/tailored-cv-preview`, buildTailoredCvRenderPayload());
      setTailoredCvPreview(res.data);
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to build the CV preview."), "error");
    } finally {
      setLoadingTailoredCvPreview(false);
    }
  };

  const exportTailoredCvPdf = async () => {
    if (!jobId) return;
    try {
      setExportingTailoredCvPdf(true);
      const response = await api.post(`/jobapplications/${jobId}/export-tailored-cv-pdf`, buildTailoredCvRenderPayload(), { responseType: "blob" });
      const blob = new Blob([response.data], { type: "application/pdf" });
      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = tailoredCvPreview?.suggestedFileName || `${(job?.jobTitle ?? "tailored-cv").replace(/\s+/g, "-").toLowerCase()}.pdf`;
      document.body.appendChild(link);
      link.click();
      link.remove();
      URL.revokeObjectURL(url);
      toast("Tailored CV PDF downloaded.", "success");
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to export the CV PDF."), "error");
    } finally {
      setExportingTailoredCvPdf(false);
    }
  };

  const savePackageWorkspace = async () => {
    if (!jobId || !job) return;

    const nextNotes = upsertApplicationAnswerDraft(job.notes, packageWorkspace.applicationAnswer);
    const draftsChanged =
      packageWorkspace.coverLetter.trim() !== savedPackageWorkspace.coverLetter.trim() ||
      packageWorkspace.applicationAnswer.trim() !== savedPackageWorkspace.applicationAnswer.trim() ||
      packageWorkspace.recruiterMessage.trim() !== savedPackageWorkspace.recruiterMessage.trim();

    if (!draftsChanged) {
      toast("No unsaved package changes.", "info");
      return;
    }

    try {
      setSavingApplicationDrafts(true);
      await api.put(`/jobapplications/${jobId}/application-drafts`, {
        coverLetterText: packageWorkspace.coverLetter,
        notes: nextNotes,
        recruiterMessageDraft: packageWorkspace.recruiterMessage,
      });

      setJob((prev) => prev ? {
        ...prev,
        coverLetterText: packageWorkspace.coverLetter,
        recruiterMessageDraft: packageWorkspace.recruiterMessage,
        notes: nextNotes,
      } : prev);
      setSavedPackageWorkspace({ ...packageWorkspace });
      readinessCache.clearCached();
      interviewPrepCache.clearCached();
      setReadiness(null);
      setInterviewPrep(null);
      toast("Application package saved to this job.", "success");
    } catch (error: any) {
      toast(getApiErrorMessage(error, "Failed to save the application package."), "error");
    } finally {
      setSavingApplicationDrafts(false);
    }
  };

  const resetPackageWorkspaceToSaved = () => {
    setPackageWorkspace(savedPackageWorkspace);
    toast("Restored the last saved package.", "info");
  };

  return (
    <Dialog open={open} onClose={onClose} fullWidth maxWidth="lg">
      <DialogTitle sx={{ pb: 1.5 }}>
        <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, flexWrap: "wrap" }}>
          <Box>
            <Typography variant="overline" sx={{ color: "text.secondary", fontWeight: 700 }}>{t("jobTableOpen")}</Typography>
            <Typography variant="h5" sx={{ fontWeight: 800, letterSpacing: "-0.01em" }}>{title}</Typography>
          </Box>
          {job && <Chip label={job.status} color={statusChipColor(job.status)} sx={{ fontWeight: 700, borderRadius: 2 }} />}
        </Box>
      </DialogTitle>

      <DialogContent>
        <JobFlowBar job={job} history={history} />
        <Box
          sx={{
            mt: 1.5,
            mb: 2,
            p: 2,
            borderRadius: 4,
            backgroundColor: "background.paper",
            boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)",
          }}
        >
          <Typography variant="body2" sx={{ color: "text.secondary" }}>{summaryFirstText}</Typography>
          {/* Phase 5: the full-page Application Workspace. The dialog stays as the quick view. */}
          {jobId && onOpenWorkspace ? (
            <Button size="small" variant="outlined" sx={{ mt: 1.5 }} onClick={() => onOpenWorkspace(jobId)}>
              Open application workspace
            </Button>
          ) : null}
        </Box>
        <Tabs
          value={tab}
          onChange={(_, v) => setTab(v)}
          sx={{ mb: 2, "& .MuiTab-root": { fontWeight: 600, textTransform: "none", minHeight: 44 } }}
          variant="scrollable"
          allowScrollButtonsMobile
        >
          <Tab label={t("jobTableOverview")} />
          <Tab label={t("jobDetailsTabCorrespondence")} />
          <Tab label={t("jobDetailsTabAttachments")} />
          <Tab label={t("jobDetailsTabTailoredCv")} />
          <Tab label={t("jobTableFollowUp")} />
          <Tab label={t("jobDetailsTabCandidateFit")} />
          <Tab label={t("jobDetailsTabFocusPlan")} />
          <Tab label={t("jobDetailsTabInterviewPrep")} />
          <Tab label={t("jobTableReadiness")} />
          {isAdmin ? <Tab label={t("jobDetailsTabHistory")} /> : null}
          <Tab label="AI Workspace" />
        </Tabs>

        {attachmentPicker}

        {statusSuggestion?.hasSuggestion ? (
          <Box sx={{ p: 1.5, mb: 2, borderRadius: 3, border: "1px solid", borderColor: "warning.main", backgroundColor: (theme) => alpha(theme.palette.warning.main, 0.08), display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1.5, flexWrap: "wrap" }}>
            <Box>
              <Typography variant="body2" sx={{ fontWeight: 800 }}>
                {t("statusSuggestionTitle", { status: statusLabel(t, statusSuggestion.suggestedStatus ?? "") })}
              </Typography>
              <Typography variant="caption" sx={{ color: "text.secondary" }}>
                {t("statusSuggestionReason", { signal: statusSuggestion.signal ?? "", current: statusLabel(t, statusSuggestion.currentStatus ?? "") })}
              </Typography>
            </Box>
            <Box sx={{ display: "flex", gap: 1 }}>
              <Button size="small" variant="contained" color="warning" disabled={applyingStatusSuggestion} onClick={() => void applyStatusSuggestion()}>
                {t("statusSuggestionApply", { status: statusLabel(t, statusSuggestion.suggestedStatus ?? "") })}
              </Button>
              <Button size="small" variant="text" onClick={() => setStatusSuggestion(null)}>{t("statusSuggestionDismiss")}</Button>
            </Box>
          </Box>
        ) : null}

        {tab === 0 && (
          <Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 2 }}>
            <Box sx={{ gridColumn: "1 / -1", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap" }}>
              <Typography variant="overline" sx={{ fontWeight: 700 }}>{t("jobDetailsStrategySnapshot")}</Typography>
              <GradientButton size="small" disabled={loadingStrategySnapshot || !canUseAi || !!focusPlanOperation && !["succeeded", "failed", "cancelled"].includes(focusPlanOperation.status)} onClick={async () => {
                if (!jobId) return;
                setLoadingStrategySnapshot(true);
                try {
                  const [fitRes, operationRes] = await Promise.all([
                    api.get<CandidateFit>(`/jobapplications/${jobId}/candidate-fit`, { params: { attachmentIds: selectedAttachmentCsv || undefined } }),
                    api.post<StrategySnapshotOperationResponse>(`/jobapplications/${jobId}/focus-plan/operations`, { attachmentIds: selectedAttachmentCsv || null }),
                  ]);
                  candidateFitCache.setCached(`${jobId}:candidate-fit:${selectedAttachmentCsv || "none"}`, fitRes.data);
                  setCandidateFit(fitRes.data);
                  focusPlanLookupVersion.current += 1;
                  announcedFocusPlanOperation.current = null;
                  setFocusPlanOperation(operationRes.data.operation);
                } catch {
                  toast(t("jobDetailsStrategySnapshotFailed"), "error");
                } finally {
                  setLoadingStrategySnapshot(false);
                }
              }}>{loadingStrategySnapshot ? t("jobDetailsRefreshing") : canUseAi ? t("jobDetailsGenerateStrategySnapshot") : "Pro required"}</GradientButton>
            </Box>
            {focusPlanOperation && focusPlanOperation.status !== "succeeded" ? (
              <Alert severity={focusPlanOperation.status === "failed" ? "error" : focusPlanOperation.status === "cancelled" ? "warning" : "info"} sx={{ gridColumn: "1 / -1" }}
                action={<>
                  {focusPlanOperation.canCancel ? <Button size="small" color="inherit" onClick={() => void cancelFocusPlan()}>Cancel</Button> : null}
                  {focusPlanOperation.canRetry ? <Button size="small" color="inherit" onClick={() => void retryFocusPlan()}>Retry</Button> : null}
                </>}>
                Strategy snapshot: {strategyOperationLabel(focusPlanOperation)}
              </Alert>
            ) : null}
            {candidateFit || focusPlan ? (
              <Box sx={{ gridColumn: "1 / -1", p: 2, borderRadius: 4, backgroundColor: "background.paper", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)" }}>
                <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", alignItems: "center", mb: 1 }}>
                  {candidateFit ? <Chip size="small" color={candidateFit.matchScore >= 75 ? "success" : candidateFit.matchScore >= 55 ? "warning" : "default"} label={t("jobDetailsMatchPercent", { count: candidateFit.matchScore })} /> : null}
                  {candidateFit?.fitLevel ? <Chip size="small" variant="outlined" label={candidateFit.fitLevel} /> : null}
                </Box>
                {focusPlan?.strategicSummary ? <Typography sx={{ whiteSpace: "pre-wrap", mb: 1 }}>{focusPlan.strategicSummary}</Typography> : null}
                {candidateFit?.matchSummary ? <Typography sx={{ color: "text.secondary", whiteSpace: "pre-wrap", mb: 1.5 }}>{candidateFit.matchSummary}</Typography> : null}
                {focusPlan?.immediatePriorities?.length ? <ListCard title={t("jobDetailsImmediatePriorities")} items={focusPlan.immediatePriorities.slice(0, 3)} /> : null}
              </Box>
            ) : (
              <Box sx={{ gridColumn: "1 / -1" }}>
                <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsStrategySnapshotEmpty")}</Typography>
              </Box>
            )}
            <Box><Typography variant="overline">{t("jobDetailsDateApplied")}</Typography><Typography>{job?.dateApplied ? new Date(job.dateApplied).toLocaleDateString() : "—"}</Typography></Box>
            <Box><Typography variant="overline">{t("jobDetailsDaysSince")}</Typography><Typography>{job?.daysSince ?? "—"}</Typography></Box>
            <Box><Typography variant="overline">{t("jobTableLocation")}</Typography><Typography>{job?.location ?? ""}</Typography></Box>
            <Box><Typography variant="overline">{t("jobDetailsSalary")}</Typography><Typography>{job?.salary ?? ""}</Typography></Box>
            <Box><Typography variant="overline">{t("jobDetailsNextAction")}</Typography><Typography>{job?.nextAction ?? ""}</Typography></Box>
            <Box><Typography variant="overline">{t("jobDetailsFollowUp")}</Typography><Typography>{job?.followUpAt ? new Date(job.followUpAt).toLocaleDateString() : ""}</Typography></Box>
            <Box><Typography variant="overline">{t("jobDetailsDeadline")}</Typography><Typography>{job?.deadline ? new Date(job.deadline).toLocaleDateString() : ""}</Typography></Box>
            <Box><Typography variant="overline">{t("jobDetailsTags")}</Typography><Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", mt: 0.5 }}>{tags.length === 0 ? <Typography sx={{ color: "text.secondary" }}>-</Typography> : tags.map((t) => <Chip key={t} label={t} size="small" />)}</Box></Box>
            <Box sx={{ gridColumn: "1 / -1" }}><Typography variant="overline">{t("jobDetailsAttachmentTypes")}</Typography><Typography>{checklist}</Typography></Box>
            <Box sx={{ gridColumn: "1 / -1" }}><Typography variant="overline">{t("jobDetailsJobUrl")}</Typography><Typography>{job?.jobUrl ? <a href={job.jobUrl} target="_blank" rel="noreferrer">{job.jobUrl}</a> : ""}</Typography></Box>
            <Box sx={{ gridColumn: "1 / -1", mt: 1 }}>
              <Box sx={{ display: "flex", justifyContent: "space-between", gap: 1, alignItems: "center", flexWrap: "wrap", mb: 0.5 }}>
                <Typography variant="overline">{t("jobDetailsSummaryAndSkills")}</Typography>
                <Button size="small" variant="outlined" disabled={refreshingAi || !canUseAi} onClick={async () => {
                  if (!jobId) return;
                  if (!(await confirmAction(t("jobDetailsRefreshAiConfirm"), { title: t("jobDetailsRefreshAiTitle"), confirmLabel: t("jobDetailsRefreshAi") }))) return;
                  setRefreshingAi(true);
                  try {
                    const res = await api.post<JobApplication>(`/jobapplications/${jobId}/refresh-ai`);
                    setJob(res.data);
                    toast(t("jobDetailsSummaryRefreshed"), "success");
                  } catch (error: any) {
                    toast(getApiErrorMessage(error, t("jobDetailsSummaryRefreshFailed")), "error");
                  } finally {
                    setRefreshingAi(false);
                  }
                }}>{refreshingAi ? t("jobDetailsRefreshing") : canUseAi ? t("jobDetailsRefreshAi") : "Pro required"}</Button>
              </Box>
              <Typography sx={{ whiteSpace: "pre-wrap" }}>{summaryFirstText}</Typography>
            </Box>
            {showTranslatedText ? (
              <Box sx={{ gridColumn: "1 / -1" }}>
                <Typography variant="overline">{t("jobDetailsTranslatedRoleText")}</Typography>
                <Typography sx={{ whiteSpace: "pre-wrap" }}>{translatedDescriptionText}</Typography>
              </Box>
            ) : null}
            {showOriginalText ? (
              <Box sx={{ gridColumn: "1 / -1" }}>
                <Typography variant="overline">{t("jobDetailsOriginalRoleText")}</Typography>
                <Typography sx={{ whiteSpace: "pre-wrap", color: "text.secondary" }}>{originalDescriptionText}</Typography>
              </Box>
            ) : null}
            <Box sx={{ gridColumn: "1 / -1" }}><Typography variant="overline">{t("editJobNotes")}</Typography><Typography sx={{ whiteSpace: "pre-wrap" }}>{job?.notes ?? ""}</Typography></Box>
          </Box>
        )}

        {tab === 1 && jobId && <Correspondence jobId={jobId} jobContext={{ companyName: job?.company?.name, recruiterEmail: job?.company?.recruiterEmail, jobTitle: job?.jobTitle }} />}
        {tab === 2 && jobId && <Attachments jobId={jobId} />}

        {tab === 3 && (
          <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
            <Box sx={{ p: 1.5, borderRadius: 3, border: "1px solid", borderColor: hasUnsavedTailoredCvDraftChanges ? "warning.main" : "divider", backgroundColor: "background.default" }}>
              <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap", mb: 1 }}>
                <Box>
                  <Typography variant="overline">Tailored CV draft</Typography>
                  <Typography variant="body2" sx={{ color: "text.secondary" }}>This draft is job-scoped. It stays separate from your master CV and from the package drafts below.</Typography>
                </Box>
                <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", alignItems: "center" }}>
                  <FormControl size="small" sx={{ minWidth: 180 }}>
                    <InputLabel>{t("jobDetailsTailoredCvMode")}</InputLabel>
                    <Select value={generationMode} label={t("jobDetailsTailoredCvMode")} onChange={(e) => setGenerationMode(e.target.value as GenerationMode)}>
                      <MenuItem value="default">{t("jobDetailsGenerationDefault")}</MenuItem>
                      <MenuItem value="concise">{t("jobDetailsGenerationConcise")}</MenuItem>
                      <MenuItem value="ats">{t("jobDetailsGenerationAts")}</MenuItem>
                      <MenuItem value="achievement">{t("jobDetailsGenerationAchievement")}</MenuItem>
                      <MenuItem value="interview">{t("jobDetailsGenerationInterview")}</MenuItem>
                    </Select>
                  </FormControl>
                  <FormControl size="small" sx={{ minWidth: 180 }}>
                    <InputLabel>Template</InputLabel>
                    <Select value={tailoredCvDraft.templateId} label="Template" onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({ ...current, templateId: e.target.value, status: "edited" }))}>
                      <MenuItem value="ats-minimal">ATS Minimal</MenuItem>
                      <MenuItem value="harvard">Harvard</MenuItem>
                      <MenuItem value="auckland">Auckland</MenuItem>
                      <MenuItem value="edinburgh">Edinburgh</MenuItem>
                    </Select>
                  </FormControl>
                  <TextField
                    size="small"
                    label="Accent"
                    type="color"
                    value={tailoredCvDraft.renderOptions.accentColor?.startsWith("#") ? tailoredCvDraft.renderOptions.accentColor : "#334155"}
                    onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({
                      ...current,
                      renderOptions: { ...current.renderOptions, accentColor: e.target.value },
                      status: "edited",
                    }))}
                    sx={{ width: 110 }}
                    InputLabelProps={{ shrink: true }}
                  />
                  <Button size="small" variant={tailoredCvDraft.renderOptions.showPhoto ? "contained" : "outlined"} onClick={() => setTailoredCvDraft((current) => normalizeTailoredCvDraft({
                    ...current,
                    renderOptions: { ...current.renderOptions, showPhoto: !current.renderOptions.showPhoto },
                    status: "edited",
                  }))}>{tailoredCvDraft.renderOptions.showPhoto ? "Photo on" : "Photo off"}</Button>
                  <Button size="small" variant={useProfilePhoto ? "contained" : "outlined"} onClick={() => setUseProfilePhoto((current) => !current)}>{useProfilePhoto ? "Using profile photo" : "Profile photo off"}</Button>
                  <Button size="small" variant="outlined" component="label">
                    Pick photo
                    <input hidden type="file" accept="image/png,image/jpeg,image/webp" onChange={(event) => {
                      const file = event.target.files?.[0];
                      if (!file) return;
                      const reader = new FileReader();
                      reader.onload = () => setCustomPhotoDataUrl(typeof reader.result === "string" ? reader.result : null);
                      reader.readAsDataURL(file);
                    }} />
                  </Button>
                  {customPhotoDataUrl ? <Button size="small" variant="text" onClick={() => setCustomPhotoDataUrl(null)}>Clear custom photo</Button> : null}
                  <Button size="small" variant="outlined" disabled={!canUseAi || loadingTailoredCvDraft || generatingTailoredCvDraft} onClick={generateTailoredCvDraft}>{generatingTailoredCvDraft ? "Generating tailored draft..." : canUseAi ? "Generate tailored draft" : "Pro required"}</Button>
                  <Button size="small" variant="outlined" disabled={loadingTailoredCvPreview} onClick={refreshTailoredCvPreview}>{loadingTailoredCvPreview ? "Building preview..." : "Preview PDF layout"}</Button>
                  <Button size="small" variant="outlined" disabled={exportingTailoredCvPdf} onClick={exportTailoredCvPdf}>{exportingTailoredCvPdf ? "Exporting PDF..." : "Download PDF"}</Button>
                  <Button size="small" variant="outlined" disabled={!hasUnsavedTailoredCvDraftChanges} onClick={resetTailoredCvDraftToSaved}>Reset to saved draft</Button>
                  <Button size="small" variant="contained" disabled={savingTailoredCvDraft || loadingTailoredCvDraft} onClick={saveTailoredCvDraft}>{savingTailoredCvDraft ? t("jobDetailsSaving") : "Save tailored draft"}</Button>
                </Box>
              </Box>
              <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", mb: 1.5 }}>
                <Chip size="small" label={`Tailored CV · ${tailoredCvDraftStatus.label}`} color={tailoredCvDraftStatus.color} />
                <Chip size="small" variant="outlined" label={`Template · ${tailoredCvDraft.templateId}`} />
                {tailoredCvDraft.isLegacyFallback ? <Chip size="small" color="warning" variant="outlined" label="Legacy text fallback" /> : null}
                {tailoredCvDraft.lastGeneratedAtUtc ? <Chip size="small" variant="outlined" label={`Generated ${new Date(tailoredCvDraft.lastGeneratedAtUtc).toLocaleString()}`} /> : null}
                {tailoredCvDraft.canonicalProfileVersion ? <Chip size="small" variant="outlined" label={`Profile v${tailoredCvDraft.canonicalProfileVersion}`} /> : null}
              </Box>

              {loadingTailoredCvDraft ? (
                <Box sx={{ py: 4, display: "flex", justifyContent: "center" }}><CircularProgress size={28} /></Box>
              ) : (
                <Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", lg: "1.1fr 0.9fr" }, gap: 2 }}>
                  <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                    <TextField
                      label="Headline"
                      value={tailoredCvDraft.headline ?? ""}
                      onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({ ...current, headline: e.target.value, status: "edited" }))}
                      fullWidth
                    />
                    <TextField
                      label="Summary bullets"
                      value={joinLines(tailoredCvDraft.summary)}
                      onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({ ...current, summary: splitLines(e.target.value), status: "edited" }))}
                      multiline
                      minRows={5}
                      fullWidth
                      helperText="One bullet per line."
                    />
                    <TextField
                      label="Selected skills"
                      value={joinLines(tailoredCvDraft.selectedSkills)}
                      onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({ ...current, selectedSkills: splitLines(e.target.value), status: "edited" }))}
                      multiline
                      minRows={4}
                      fullWidth
                      helperText="One skill per line."
                    />
                    <TextField
                      label="Experience"
                      value={tailoredCvDraft.experience.map((item) => [
                        [item.title, item.company].filter(Boolean).join(" — "),
                        [item.location, item.start, item.end].filter(Boolean).join(" | "),
                        ...(item.bullets ?? []).map((bullet) => `- ${bullet}`),
                      ].filter(Boolean).join("\n")).join("\n\n")}
                      onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({
                        ...current,
                        experience: e.target.value
                          .split(/\n\s*\n/)
                          .map((block) => block.trim())
                          .filter(Boolean)
                          .map((block) => {
                            const lines = block.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
                            const [titleCompany = "", meta = "", ...bulletLines] = lines;
                            const [title = "", company = ""] = titleCompany.split("—").map((part) => part.trim());
                            const [location = "", start = "", end = ""] = meta.split("|").map((part) => part.trim());
                            return {
                              title,
                              company,
                              location,
                              start,
                              end,
                              bullets: bulletLines.map((line) => line.replace(/^[-•*]\s*/, "").trim()).filter(Boolean),
                            };
                          }),
                        status: "edited",
                      }))}
                      multiline
                      minRows={10}
                      fullWidth
                      helperText="Separate entries with a blank line. First line: Title — Company. Second line: Location | Start | End."
                    />
                    <TextField
                      label="Education"
                      value={tailoredCvDraft.education.map((item) => [
                        [item.qualification, item.institution].filter(Boolean).join(" — "),
                        [item.location, item.start, item.end].filter(Boolean).join(" | "),
                        ...(item.details ?? []).map((detail) => `- ${detail}`),
                      ].filter(Boolean).join("\n")).join("\n\n")}
                      onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({
                        ...current,
                        education: e.target.value
                          .split(/\n\s*\n/)
                          .map((block) => block.trim())
                          .filter(Boolean)
                          .map((block) => {
                            const lines = block.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
                            const [qualificationInstitution = "", meta = "", ...detailLines] = lines;
                            const [qualification = "", institution = ""] = qualificationInstitution.split("—").map((part) => part.trim());
                            const [location = "", start = "", end = ""] = meta.split("|").map((part) => part.trim());
                            return {
                              qualification,
                              institution,
                              location,
                              start,
                              end,
                              details: detailLines.map((line) => line.replace(/^[-•*]\s*/, "").trim()).filter(Boolean),
                            };
                          }),
                        status: "edited",
                      }))}
                      multiline
                      minRows={8}
                      fullWidth
                      helperText="Separate entries with a blank line. First line: Qualification — Institution. Second line: Location | Start | End."
                    />
                    <TextField
                      label="Custom sections"
                      value={tailoredCvDraft.customSections.map((section) => `${section.title || "Additional Information"}\n${(section.items ?? []).join("\n")}`).join("\n\n")}
                      onChange={(e) => setTailoredCvDraft((current) => normalizeTailoredCvDraft({
                        ...current,
                        customSections: e.target.value
                          .split(/\n\s*\n/)
                          .map((block) => block.trim())
                          .filter(Boolean)
                          .map((block) => {
                            const [title = "", ...items] = block.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
                            return { title, items };
                          }),
                        status: "edited",
                      }))}
                      multiline
                      minRows={7}
                      fullWidth
                      helperText="Each block starts with the section title, followed by one item per line."
                    />
                  </Box>

                  <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                    <Box sx={{ p: 1.5, borderRadius: 3, boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", backgroundColor: "background.paper" }}>
                      <Typography variant="overline">Rendered CV snapshot</Typography>
                      <Typography variant="body2" sx={{ color: "text.secondary", mb: 1.5 }}>This plain-text snapshot stays deterministic and is what the job stores immediately after saving the draft.</Typography>
                      <TextField value={tailoredCvDraft.renderedText} multiline minRows={12} fullWidth InputProps={{ readOnly: true }} />
                      <Typography variant="caption" sx={{ color: "text.secondary", mt: 1, display: "block" }}>{t("jobDetailsLastUpdated", { value: job?.tailoredCvUpdatedAt ? new Date(job.tailoredCvUpdatedAt).toLocaleString() : t("jobDetailsNotSavedYet") })}</Typography>
                    </Box>
                    <Box sx={{ p: 1.5, borderRadius: 3, boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", backgroundColor: "background.paper" }}>
                      <Typography variant="overline">PDF-style preview</Typography>
                      <Typography variant="body2" sx={{ color: "text.secondary", mb: 1.5 }}>Preview and PDF export use the same HTML template contract. Accent color and photo settings apply here.</Typography>
                      {tailoredCvPreview ? (
                        <iframe title="Tailored CV preview" srcDoc={tailoredCvPreview.html} style={{ width: "100%", minHeight: 780, border: "1px solid rgba(15,23,42,0.08)", borderRadius: 12, background: "white" }} />
                      ) : (
                        <Typography sx={{ color: "text.secondary" }}>Build the PDF layout preview to inspect the ATS template before downloading.</Typography>
                      )}
                    </Box>
                    <Box sx={{ p: 1.5, borderRadius: 3, boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", backgroundColor: "background.paper" }}>
                      <Typography variant="overline">Saved job material</Typography>
                      <Typography variant="body2" sx={{ color: "text.secondary", mb: 1.5 }}>Saving the tailored draft updates the job-scoped CV text without touching your master profile.</Typography>
                      <Typography variant="body2"><strong>Tailored CV:</strong> {(job?.tailoredCvText ?? "").trim() ? "Saved on this job" : "Not saved yet"}</Typography>
                      <Typography variant="body2"><strong>Master CV:</strong> Never overwritten here</Typography>
                      <Typography variant="body2"><strong>Photo source:</strong> {customPhotoDataUrl ? "Custom preview photo" : useProfilePhoto && profileAvatarImageDataUrl ? "Profile picture" : "No photo source selected"}</Typography>
                    </Box>
                  </Box>
                </Box>
              )}
            </Box>

            <Box sx={{ p: 1.5, borderRadius: 3, border: "1px solid", borderColor: hasUnsavedPackageChanges ? "warning.main" : "divider", backgroundColor: "background.default" }}>
              <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap", mb: 1 }}>
                <Box>
                  <Typography variant="overline">Application package drafts</Typography>
                  <Typography variant="body2" sx={{ color: "text.secondary" }}>These drafts stay separate from the tailored CV draft. Save them when you want reusable role-specific copy on the job.</Typography>
                </Box>
                <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", alignItems: "center" }}>
                  <FormControl size="small" sx={{ minWidth: 190 }}>
                    <InputLabel>{t("jobDetailsCoverLetterStyle")}</InputLabel>
                    <Select value={coverLetterStyle} label={t("jobDetailsCoverLetterStyle")} onChange={(e) => setCoverLetterStyle(e.target.value as CoverLetterStyle)}>
                      <MenuItem value="balanced">{t("jobDetailsCoverLetterStyleBalanced")}</MenuItem>
                      <MenuItem value="concise">{t("jobDetailsCoverLetterStyleConcise")}</MenuItem>
                      <MenuItem value="formal">{t("jobDetailsCoverLetterStyleFormal")}</MenuItem>
                      <MenuItem value="bold">{t("jobDetailsCoverLetterStyleBold")}</MenuItem>
                    </Select>
                  </FormControl>
                  <Button size="small" variant="outlined" disabled={generatingPackage || !canUseAi} onClick={async () => {
                    if (!jobId) return;
                    setGeneratingPackage(true);
                    try {
                      const res = await api.post<ApplicationPackageResponse>(`/jobapplications/${jobId}/generate-application-package`, null, { params: { mode: generationMode, coverLetterStyle, attachmentIds: selectedAttachmentIds.join(",") || undefined } });
                      setApplicationPackage(res.data);
                      setPackageWorkspace({
                        coverLetter: res.data.coverLetterDraft ?? "",
                        applicationAnswer: res.data.applicationAnswerDraft ?? "",
                        recruiterMessage: res.data.recruiterMessageDraft ?? "",
                      });
                      setPackageGeneratedAt(new Date().toISOString());
                      toast(t("jobDetailsPackageGenerated"), "success");
                    } catch (error: any) {
                      toast(getApiErrorMessage(error, t("jobDetailsPackageGenerationFailed")), "error");
                    } finally {
                      setGeneratingPackage(false);
                    }
                  }}>{generatingPackage ? t("jobDetailsGeneratingPackage") : canUseAi ? t("jobDetailsGeneratePackage") : "Pro required"}</Button>
                  <Button size="small" variant="outlined" disabled={!hasUnsavedPackageChanges} onClick={resetPackageWorkspaceToSaved}>Reset to saved</Button>
                  <Button size="small" variant="contained" disabled={savingApplicationDrafts} onClick={savePackageWorkspace}>{savingApplicationDrafts ? t("jobDetailsSaving") : "Save package drafts"}</Button>
                </Box>
              </Box>
              <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", mb: 1.5 }}>
                <Chip size="small" label={`Cover letter · ${coverLetterStatus.label}`} color={coverLetterStatus.color} />
                <Chip size="small" label={`Application answer · ${applicationAnswerStatus.label}`} color={applicationAnswerStatus.color} />
                <Chip size="small" label={`Recruiter message · ${recruiterMessageStatus.label}`} color={recruiterMessageStatus.color} />
                <Chip size="small" variant="outlined" label="Saved package material feeds follow-up drafting" />
                {packageGeneratedAt ? <Chip size="small" variant="outlined" label={`Generated ${new Date(packageGeneratedAt).toLocaleTimeString()}`} /> : null}
              </Box>

              <Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" }, gap: 2 }}>
                <WorkspaceDraftCard
                  title={t("jobDetailsCoverLetterDraft")}
                  value={packageWorkspace.coverLetter}
                  onChange={(value) => setPackageWorkspace((current) => ({ ...current, coverLetter: value }))}
                  statusLabel={coverLetterStatus.label}
                  statusColor={coverLetterStatus.color}
                />
                <WorkspaceDraftCard
                  title={t("jobDetailsShortApplicationAnswer")}
                  value={packageWorkspace.applicationAnswer}
                  onChange={(value) => setPackageWorkspace((current) => ({ ...current, applicationAnswer: value }))}
                  statusLabel={applicationAnswerStatus.label}
                  statusColor={applicationAnswerStatus.color}
                />
                <WorkspaceDraftCard
                  title={t("jobDetailsRecruiterMessageDraft")}
                  value={packageWorkspace.recruiterMessage}
                  onChange={(value) => setPackageWorkspace((current) => ({ ...current, recruiterMessage: value }))}
                  statusLabel={recruiterMessageStatus.label}
                  statusColor={recruiterMessageStatus.color}
                />
                <Box sx={{ p: 1.5, borderRadius: 3, boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", backgroundColor: "background.paper" }}>
                  <Typography variant="overline">Saved working material</Typography>
                  <Typography variant="body2" sx={{ color: "text.secondary", mb: 1.5 }}>These saved copies are what follow-up drafting and later slices can trust and reuse.</Typography>
                  <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
                    <Typography variant="body2"><strong>Cover letter:</strong> {savedPackageWorkspace.coverLetter.trim() ? "Saved on this job" : "Not saved yet"}</Typography>
                    <Typography variant="body2"><strong>Application answer:</strong> {savedPackageWorkspace.applicationAnswer.trim() ? "Saved on this job" : "Not saved yet"}</Typography>
                    <Typography variant="body2"><strong>Recruiter message:</strong> {savedPackageWorkspace.recruiterMessage.trim() ? "Saved on this job" : "Not saved yet"}</Typography>
                  </Box>
                </Box>
                <ListCard title={t("jobDetailsKeyPoints")} items={applicationPackage?.keyPoints ?? ["Generate a package to pull in role-specific talking points."]} />
                <ListCard title={t("jobDetailsCoverLetterVariants")} items={applicationPackage?.coverLetterVariants?.length ? applicationPackage.coverLetterVariants : [t("jobDetailsNoDraftAvailable")]} />
                <ListCard title={t("jobDetailsRecruiterMessageVariants")} items={applicationPackage?.recruiterMessageVariants?.length ? applicationPackage.recruiterMessageVariants : [t("jobDetailsNoDraftAvailable")]} />
                <ListCard title={t("jobDetailsAttachmentSignals")} items={applicationPackage?.attachmentSignals?.length ? applicationPackage.attachmentSignals : [t("jobDetailsNoAttachmentSignals")]} subtitle={applicationPackage?.attachmentFilesUsed?.length ? applicationPackage.attachmentFilesUsed.join(", ") : undefined} />
              </Box>
            </Box>
          </Box>
        )}

        {tab === 4 && (
          <Box>
            {loadingDraft ? <Box sx={{ py: 4, display: "flex", justifyContent: "center" }}><CircularProgress size={28} /></Box> : followUpDraft ? (
              <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                <Box sx={{ p: 1.5, borderRadius: 3, boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", backgroundColor: "background.default" }}>
                  <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, flexWrap: "wrap", mb: 1 }}>
                    <Typography variant="overline">Follow-up context</Typography>
                    <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap" }}>
                      {followUpDraft.threadSubject ? <Chip size="small" variant="outlined" label={`Thread: ${followUpDraft.threadSubject}`} /> : null}
                      {followUpDraft.lastCorrespondenceAt ? <Chip size="small" variant="outlined" label={`Last activity: ${new Date(followUpDraft.lastCorrespondenceAt).toLocaleDateString()}`} /> : null}
                    </Box>
                  </Box>
                  <Typography sx={{ whiteSpace: "pre-wrap", mb: 1.5 }}>{followUpDraft.contextSummary}</Typography>
                  <Box sx={{ display: "grid", gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2 }}>
                    <Box><Typography variant="overline">Why now</Typography><Typography>{followUpDraft.reason}</Typography></Box>
                    <Box><Typography variant="overline">Last sender</Typography><Typography>{followUpDraft.lastCorrespondenceFrom ?? "No imported sender yet"}</Typography></Box>
                  </Box>
                  {followUpDraft.contextSignals?.length ? <Box sx={{ mt: 1.5 }}><ListCard title="Draft grounding" items={followUpDraft.contextSignals} /></Box> : null}
                </Box>
                <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', alignItems: 'center' }}>
                  <FormControl size="small" sx={{ minWidth: 240 }}>
                    <InputLabel>{t("jobDetailsFollowUpMode")}</InputLabel>
                    <Select value={followUpMode} label={t("jobDetailsFollowUpMode")} onChange={(e) => setFollowUpMode(e.target.value)}>
                      <MenuItem value="post-apply">{t("jobDetailsFollowUpModePostApply")}</MenuItem>
                      <MenuItem value="waiting-update">{t("jobDetailsFollowUpModeWaiting")}</MenuItem>
                      <MenuItem value="post-interview">{t("jobDetailsFollowUpModePostInterview")}</MenuItem>
                      <MenuItem value="offer-checkin">{t("jobDetailsFollowUpModeOffer")}</MenuItem>
                      <MenuItem value="feedback-request">{t("jobDetailsFollowUpModeFeedback")}</MenuItem>
                    </Select>
                  </FormControl>
                  <Button variant="outlined" onClick={() => setDraftReloadToken((value) => value + 1)}>{t("jobDetailsRegenerateDraft")}</Button>
                </Box>
                <Box sx={{ p: 1.5, borderRadius: 3, border: "1px solid", borderColor: "warning.main", backgroundColor: "background.default" }}>
                  <Typography variant="overline">Manual send boundary</Typography>
                  <Typography variant="body2" sx={{ color: "text.secondary" }}>
                    Generating or regenerating this grounded draft never sends recruiter email. Copy it or open Job email, where the connected provider and final confirmation are shown before delivery.
                  </Typography>
                </Box>
                <TextField label={t("jobDetailsRecipient")} value={draftRecipient} onChange={(e) => setDraftRecipient(e.target.value)} helperText={`${t("jobDetailsRecipientHelp")} This draft is not sent from the Follow up tab.`} />
                <TextField label={t("jobDetailsSubject")} value={draftSubject} onChange={(e) => setDraftSubject(e.target.value)} />
                <TextField label={t("jobDetailsDraft")} multiline minRows={8} value={draftBody} onChange={(e) => setDraftBody(e.target.value)} helperText="You can edit and copy this draft. Provider delivery is available only from Job email." />
                <Box sx={{ display: "flex", justifyContent: "space-between", gap: 1, flexWrap: "wrap" }}>
                  <Button variant="outlined" onClick={() => navigator.clipboard.writeText(`${draftSubject}\n\n${draftBody}`)}>{t("jobDetailsCopyDraft")}</Button>
                  <Button variant="contained" href="/correspondence">{t("jobDetailsOpenJobEmail")}</Button>
                </Box>
              </Box>
            ) : <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsNoDraftAvailable")}</Typography>}
          </Box>
        )}

        {!canUseAi && [4, 5, 6, 7].includes(tab) && <Alert severity="info" sx={{ mb: 2 }} action={<Button href="/settings" size="small">View Pro</Button>}>AI assistance on this tab requires Pro. Non-AI job data and manual editing remain available.</Alert>}
        <JobInsightTabs
          tab={tab}
          matchScore={matchScore}
          loadingMatchScore={loadingMatchScore}
          updateLearningRecommendation={updateLearningRecommendation}
          candidateFit={candidateFit}
          loadingCandidateFit={loadingCandidateFit}
          regenerateCandidateFit={regenerateCandidateFit}
          fitLevel={fitLevel}
          focusPlan={focusPlan}
          loadingFocusPlan={loadingFocusPlan || !!focusPlanOperation && !["succeeded", "failed", "cancelled"].includes(focusPlanOperation.status)}
          regenerateFocusPlan={regenerateFocusPlan}
          focusPlanOperation={focusPlanOperation}
          cancelFocusPlan={() => void cancelFocusPlan()}
          retryFocusPlan={() => void retryFocusPlan()}
          interviewPrep={interviewPrep}
          loadingInterviewPrep={loadingInterviewPrep}
          regenerateInterviewPrep={regenerateInterviewPrep}
          readiness={readiness}
          loadingReadiness={loadingReadiness}
        />

        {tab === 9 && isAdmin && (
          <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
            {history.length === 0 ? <Typography sx={{ color: "text.secondary" }}>{t("jobDetailsNoHistory")}</Typography> : history.map((entry) => <PaperRow key={entry.id} type={entry.type} oldValue={entry.oldValue} newValue={entry.newValue} at={entry.at} note={entry.note} />)}
          </Box>
        )}
        {/* AI Workspace is the last tab: index depends on whether the admin History tab is present. */}
        {tab === (isAdmin ? 10 : 9) && jobId && <AiWorkspacePanel jobId={jobId} />}
      </DialogContent>
    </Dialog>
  );
}

function strategyOperationLabel(operation: UserOperation) {
  if (operation.cancellationRequestedAtUtc) return "cancellation requested";
  switch (operation.status) {
    case "queued": return "queued";
    case "running": return "processing locally";
    case "waiting_for_retry": return "waiting to retry";
    case "waiting_for_external_fallback": return "waiting for approved fallback";
    case "failed": return `failed${operation.failureCategory ? ` (${operation.failureCategory.replaceAll("_", " ")})` : ""}`;
    case "cancelled": return "cancelled";
    default: return "completed";
  }
}
