import React, { useMemo, useState } from "react";

import { Box, Button, Paper, Typography } from "@mui/material";

import { api } from "../api";
import { Company, JobApplication } from "../types";
import { useToast } from "../toast";
import { useI18n } from "../i18n/I18nProvider";

// savedAt is omitted alongside id: both are assigned by the server on create, so an imported
// row has no business supplying one.
type ImportJob = Omit<JobApplication, "id" | "company" | "savedAt"> & {
  company: Pick<Company, "name" | "location" | "source">;
};

function downloadText(filename: string, text: string, mime: string) {
  const blob = new Blob([text], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  a.click();
  setTimeout(() => URL.revokeObjectURL(url), 0);
}

function parseCsv(text: string): string[][] {
  const rows: string[][] = [];
  let row: string[] = [];
  let cur = "";
  let i = 0;
  let inQuotes = false;

  const pushCell = () => {
    row.push(cur);
    cur = "";
  };
  const pushRow = () => {
    if (row.length === 1 && row[0] === "" && rows.length > 0) {
      row = [];
      return;
    }
    rows.push(row);
    row = [];
  };

  while (i < text.length) {
    const ch = text[i];
    if (inQuotes) {
      if (ch === '"') {
        const next = text[i + 1];
        if (next === '"') {
          cur += '"';
          i += 2;
          continue;
        }
        inQuotes = false;
        i += 1;
        continue;
      }
      cur += ch;
      i += 1;
      continue;
    }

    if (ch === '"') {
      inQuotes = true;
      i += 1;
      continue;
    }
    if (ch === ",") {
      pushCell();
      i += 1;
      continue;
    }
    if (ch === "\r") {
      i += 1;
      continue;
    }
    if (ch === "\n") {
      pushCell();
      pushRow();
      i += 1;
      continue;
    }
    cur += ch;
    i += 1;
  }
  pushCell();
  pushRow();
  return rows;
}

function parseDateDMY(s: string): string | null {
  const v = (s || "").trim();
  if (!v) return null;
  const m = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(v);
  if (!m) return null;
  const dd = Number(m[1]);
  const mm = Number(m[2]);
  const yyyy = Number(m[3]);
  if (!yyyy || mm < 1 || mm > 12 || dd < 1 || dd > 31) return null;
  return `${String(yyyy).padStart(4, "0")}-${String(mm).padStart(2, "0")}-${String(dd).padStart(2, "0")}`;
}

function csvToImportJobs(csvText: string): ImportJob[] {
  const rows = parseCsv(csvText);
  if (!rows.length) return [];
  const header = rows[0].map((h) => h.trim());
  const idx = (name: string) => header.findIndex((h) => h.toLowerCase() === name.toLowerCase());

  const iJobTitle = idx("Job Title");
  const iCompany = idx("Company");
  const iLocation = idx("Location");
  const iSource = idx("Source");
  const iDateApplied = idx("Date Applied");
  const iMethod = idx("Application Method");
  const iCover = idx("Cover Letter / Resume");
  const iResp = idx("Response Received");
  const iRespDate = idx("Response Date");
  const iStatus = idx("Status");
  const iNotes = idx("Notes");
  const iInterviewDate = idx("Interview Date");

  const out: ImportJob[] = [];
  for (let r = 1; r < rows.length; r++) {
    const row = rows[r];
    const get = (i: number) => (i >= 0 ? (row[i] ?? "").trim() : "");

    const jobTitle = get(iJobTitle);
    const companyName = get(iCompany) || jobTitle;
    if (!jobTitle && !companyName) continue;

    const rawStatus = get(iStatus);
    const status = rawStatus === "Follow-up Needed" ? "Waiting" : rawStatus || "Applied";
    const dateApplied = parseDateDMY(get(iDateApplied)) ?? new Date().toISOString().slice(0, 10);
    const responseReceived = /^yes$/i.test(get(iResp));
    const responseDate = parseDateDMY(get(iRespDate)) ?? undefined;
    const interviewDate = parseDateDMY(get(iInterviewDate)) ?? "";

    const notesParts: string[] = [];
    const baseNotes = get(iNotes);
    if (baseNotes) notesParts.push(baseNotes);
    const method = get(iMethod);
    if (method) notesParts.push(`Application Method: ${method}`);
    const cover = get(iCover);
    if (cover) notesParts.push(`Cover Letter / Resume: ${cover}`);
    if (interviewDate) notesParts.push(`Interview Date: ${interviewDate}`);
    if (responseDate) notesParts.push(`Response Date: ${responseDate}`);

    out.push({
      jobTitle: jobTitle || "(missing title)",
      status,
      dateApplied,
      responseReceived,
      responseDate,
      notes: notesParts.join("\n") || undefined,
      coverLetterText: undefined,
      jobUrl: undefined,
      daysSince: 0,
      company: {
        name: companyName || "(missing company)",
        location: get(iLocation) || undefined,
        source: get(iSource) || undefined,
      },
      location: get(iLocation) || undefined,
      salary: undefined,
      nextAction: status === "Waiting" ? "Follow up" : undefined,
      followUpAt: undefined,
      isDeleted: false,
      deletedAt: undefined,
    });
  }

  return out;
}

export default function ImportExportJobs() {
  const { toast } = useToast();
  const { t } = useI18n();
  const [importing, setImporting] = useState(false);
  const [lastImportCount, setLastImportCount] = useState<number | null>(null);

  const exportAll = async (format: "json" | "csv") => {
    try {
      const stamp = new Date().toISOString().slice(0, 10);
      const url = `/export/jobs?format=${format}&includeDeleted=false`;
      const res = await api.get(url, { responseType: "blob" });

      const text = await res.data.text();
      downloadText(`job-tracker-export-${stamp}.${format}`, text, format === "json" ? "application/json" : "text/csv");
      toast(t("exportedJobs", { format: format.toUpperCase() }), "success");
    } catch {
      toast(t("exportFailed"), "error");
    }
  };

  const onImportFile = async (file: File | null) => {
    if (!file) return;
    setImporting(true);
    setLastImportCount(null);
    try {
      const text = await file.text();
      const parsed = JSON.parse(text) as ImportJob[];
      if (!Array.isArray(parsed)) throw new Error("Invalid format");

      let created = 0;
      for (const j of parsed) {
        const companyRes = await api.post<Company>("/companies", j.company);
        const company = companyRes.data;
        await api.post("/jobapplications", {
          jobTitle: j.jobTitle,
          companyId: company.id,
          status: j.status,
          dateApplied: j.dateApplied,
          location: j.location,
          salary: j.salary,
          nextAction: j.nextAction,
          followUpAt: j.followUpAt,
          jobUrl: j.jobUrl,
          notes: j.notes,
          coverLetterText: j.coverLetterText,
        });
        created += 1;
      }

      setLastImportCount(created);
      toast(t("importedJobs", { count: created }), "success");
    } catch {
      toast(t("importFailedJson"), "error");
    } finally {
      setImporting(false);
    }
  };

  const onConvertCsv = async (file: File | null) => {
    if (!file) return;
    try {
      const text = await file.text();
      const jobs = csvToImportJobs(text);
      const stamp = new Date().toISOString().slice(0, 10);
      downloadText(`job-tracker-import-${stamp}.json`, JSON.stringify(jobs, null, 2), "application/json");
      toast(t("convertedRows", { count: jobs.length }), "success");
    } catch {
      toast(t("csvConversionFailed"), "error");
    }
  };

  const helper = useMemo(() => t("importExportBody"), [t]);

  return (
    <Paper sx={{ mt: 2, p: 2 }}>
      <Typography variant="h6" sx={{ mb: 1 }}>
        {t("importExportTitle")}
      </Typography>
      <Typography variant="body2" sx={{ color: "text.secondary", mb: 2 }}>
        {helper}
      </Typography>

      <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", alignItems: "center" }}>
        <Button variant="outlined" onClick={() => void exportAll("json")}>{t("exportJson")}</Button>
        <Button variant="outlined" onClick={() => void exportAll("csv")}>{t("exportCsv")}</Button>

        <Button variant="contained" component="label" disabled={importing}>
          {importing ? t("profileUploading") : t("importJson")}
          <input type="file" accept="application/json" hidden onChange={(e) => void onImportFile(e.target.files?.[0] ?? null)} />
        </Button>

        <Button variant="outlined" component="label">
          {t("convertCsvToImportJson")}
          <input type="file" accept=".csv,text/csv" hidden onChange={(e) => void onConvertCsv(e.target.files?.[0] ?? null)} />
        </Button>

        {lastImportCount !== null && (
          <Typography sx={{ ml: 1, color: "text.secondary" }}>
            {t("lastImport", { count: lastImportCount })}
          </Typography>
        )}
      </Box>
    </Paper>
  );
}
