import { createHash } from "crypto";
import { NextResponse } from "next/server";
import { ensureCountryAccess, getViewerContextOrNull } from "@/lib/auth";
import { env } from "@/lib/env";
import { mergeIncomingRows } from "@/lib/import/merge";
import { parseExcelImport } from "@/lib/import/parse";
import { ensureUploadScopeCountries, parseUploadScope } from "@/lib/import/scope";
import {
  getLocalDb,
  mergeIntoLocalRawEvents,
  nextImportId,
  pushProcessingImport,
  saveLocalDb,
  saveLocalUpload,
  upsertLocalDocumentBrands,
  upsertLocalEmailSentiment,
  updateImport,
} from "@/lib/local-db";
import { createAdminClient } from "@/lib/supabase/admin";
import { createClient } from "@/lib/supabase/server";
import { slugify } from "@/lib/utils";

function extensionOf(fileName: string) {
  const ext = fileName.split(".").pop()?.toLowerCase();
  return ext && ext.length <= 5 ? ext : "xlsx";
}

const MAX_IMPORT_FILE_BYTES = 25 * 1024 * 1024;
const ALLOWED_IMPORT_EXTENSIONS = new Set(["xlsx", "xls", "csv"]);

function isSupportedImportFile(file: File) {
  const ext = extensionOf(file.name);
  return ALLOWED_IMPORT_EXTENSIONS.has(ext);
}

function compressSummary(warnings: string[], errors: string[]) {
  const all = [...errors, ...warnings];
  if (all.length === 0) return null;
  return all.join("\n").slice(0, 8000);
}

function normalizeDocumentName(value: string) {
  return value.trim().replace(/\s+/g, " ");
}

function extractDocumentBrands(
  rows: Array<{ approved_document_name: string }>,
  rawDocumentBrands: Record<string, string>,
) {
  const docNames = new Set(rows.map((row) => normalizeDocumentName(row.approved_document_name)));
  const filtered: Record<string, string> = {};

  for (const [approvedDocumentName, brand] of Object.entries(rawDocumentBrands)) {
    const docName = normalizeDocumentName(approvedDocumentName);
    const normalizedBrand = brand.trim();
    if (!docName || !normalizedBrand) continue;
    if (!docNames.has(docName)) continue;
    filtered[docName] = normalizedBrand;
  }

  return filtered;
}

export async function POST(request: Request) {
  const viewer = await getViewerContextOrNull();
  if (!viewer) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }
  if (viewer.role !== "admin") {
    return NextResponse.json({ message: "Only admins can import data." }, { status: 403 });
  }

  const formData = await request.formData();
  const file = formData.get("file");
  let uploadScope = null;

  try {
    uploadScope = parseUploadScope(formData.get("scope"));
  } catch (error) {
    return NextResponse.json(
      { message: error instanceof Error ? error.message : "Invalid upload scope." },
      { status: 400 },
    );
  }

  if (!(file instanceof File)) {
    return NextResponse.json({ message: "No file provided." }, { status: 400 });
  }

  if (!isSupportedImportFile(file)) {
    return NextResponse.json(
      { message: "Unsupported file type. Upload .xlsx, .xls, or .csv." },
      { status: 400 },
    );
  }

  if (file.size > MAX_IMPORT_FILE_BYTES) {
    return NextResponse.json({ message: "File too large. Max size is 25MB." }, { status: 400 });
  }

  const arrayBuffer = await file.arrayBuffer();
  const fileHash = createHash("sha256").update(Buffer.from(arrayBuffer)).digest("hex");

  if (!env.hasSupabasePublicEnv) {
    const db = await getLocalDb();
    const importId = nextImportId(db);
    const uploadPath = await saveLocalUpload(importId, file.name, new Uint8Array(arrayBuffer));

    pushProcessingImport(db, {
      id: importId,
      uploadedBy: viewer.userId,
      fileName: file.name,
      fileHash,
      storagePath: uploadPath,
    });

    const parsed = parseExcelImport(arrayBuffer);
    const skippedRows = parsed.warnings.filter((warning) => warning.includes("Row ")).length;

    if (parsed.errors.length > 0 || (parsed.rows.length === 0 && parsed.sentimentRows === 0)) {
      updateImport(db, importId, {
        status: "failed",
        total_rows: parsed.rows.length + parsed.sentimentRows,
        failed_rows: parsed.errors.length > 0 ? parsed.errors.length : 1,
        detected_country_code:
          parsed.detectedCountries.length === 0
            ? null
            : parsed.detectedCountries.length === 1
              ? parsed.detectedCountries[0]
              : "MULTI",
        error_summary: compressSummary(parsed.warnings, parsed.errors),
      });
      await saveLocalDb(db);
      return NextResponse.json(
        { message: parsed.errors[0] ?? "No valid rows found.", importId },
        { status: 400 },
      );
    }

    try {
      if (parsed.detectedCountries.length > 0) {
        await ensureCountryAccess(parsed.detectedCountries, viewer);
        if (viewer.role === "admin") {
          ensureUploadScopeCountries(parsed.detectedCountries, uploadScope);
        }
      }
    } catch (error) {
      updateImport(db, importId, {
        status: "failed",
        total_rows: parsed.rows.length + parsed.sentimentRows,
        failed_rows: parsed.rows.length,
        detected_country_code:
          parsed.detectedCountries.length === 0
            ? null
            : parsed.detectedCountries.length === 1
              ? parsed.detectedCountries[0]
              : "MULTI",
        error_summary: compressSummary(parsed.warnings, [
          error instanceof Error ? error.message : "Country access denied.",
        ]),
      });
      await saveLocalDb(db);
      return NextResponse.json(
        { message: error instanceof Error ? error.message : "Country access denied.", importId },
        { status: 403 },
      );
    }

    const mergedRows = mergeIncomingRows(parsed.rows);
    const parsedDocumentBrands = extractDocumentBrands(mergedRows, parsed.documentBrands);
    const localCounts =
      mergedRows.length > 0
        ? mergeIntoLocalRawEvents(db, mergedRows, importId)
        : { insertedRows: 0, updatedRows: 0, failedRows: 0 };
    upsertLocalDocumentBrands(db, parsedDocumentBrands);
    const sentimentCounts = upsertLocalEmailSentiment(db, parsed.sentimentByEmail);
    const failedRows = localCounts.failedRows + skippedRows;

    updateImport(db, importId, {
      status: "completed",
      total_rows: parsed.rows.length + parsed.sentimentRows,
      inserted_rows: localCounts.insertedRows,
      updated_rows: localCounts.updatedRows,
      failed_rows: failedRows,
      detected_country_code:
        parsed.detectedCountries.length === 0
          ? null
          : parsed.detectedCountries.length === 1
            ? parsed.detectedCountries[0]
            : "MULTI",
      error_summary: compressSummary(parsed.warnings, parsed.errors),
    });

    await saveLocalDb(db);

    return NextResponse.json({
      importId,
      insertedRows: localCounts.insertedRows,
      updatedRows: localCounts.updatedRows,
      failedRows,
      warnings: parsed.warnings,
      mode: "local",
      sentimentRows: parsed.sentimentRows,
      sentimentEmailCount: Object.keys(parsed.sentimentByEmail).length,
      sentimentUpdatedRows: sentimentCounts.inserted + sentimentCounts.updated,
    });
  }

  const supabase = await createClient();

  const { data: importRow, error: importInsertError } = await supabase
    .from("imports")
    .insert({
      uploaded_by: viewer.userId,
      file_name: file.name,
      file_hash: fileHash,
      status: "processing",
      total_rows: 0,
      inserted_rows: 0,
      updated_rows: 0,
      failed_rows: 0,
    })
    .select("id")
    .single();

  if (importInsertError || !importRow) {
    return NextResponse.json(
      { message: importInsertError?.message ?? "Failed to create import record." },
      { status: 500 },
    );
  }

  const importId = importRow.id as number;
  const ext = extensionOf(file.name);
  const baseName = file.name.replace(/\.[^.]+$/, "");
  const storagePath = `${viewer.userId}/${importId}/${slugify(baseName || "upload")}.${ext}`;

  const failImport = async (message: string, warnings: string[] = []) => {
    await supabase
      .from("imports")
      .update({
        status: "failed",
        storage_path: storagePath,
        error_summary: compressSummary(warnings, [message]),
      })
      .eq("id", importId);
  };

  const { error: storageError } = await supabase.storage.from("imports").upload(storagePath, file, {
    contentType: file.type || "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    upsert: false,
  });

  if (storageError) {
    await failImport(storageError.message);
    return NextResponse.json({ message: storageError.message }, { status: 500 });
  }

  const parsed = parseExcelImport(arrayBuffer);
  const skippedRows = parsed.warnings.filter((warning) => warning.includes("Row ")).length;

  if (parsed.errors.length > 0 || parsed.rows.length === 0) {
    const sentimentOnly = parsed.rows.length === 0 && parsed.sentimentRows > 0;
    const message = sentimentOnly
      ? "This file only contains sentiment actions. Supabase mode does not support sentiment-only imports yet."
      : parsed.errors[0] ?? "No valid rows found.";
    await supabase
      .from("imports")
      .update({
        status: "failed",
        storage_path: storagePath,
        total_rows: parsed.rows.length,
        failed_rows: parsed.errors.length > 0 ? parsed.errors.length : 1,
        detected_country_code:
          parsed.detectedCountries.length === 0
            ? null
            : parsed.detectedCountries.length === 1
              ? parsed.detectedCountries[0]
              : "MULTI",
        error_summary: compressSummary(parsed.warnings, [message]),
      })
      .eq("id", importId);

    return NextResponse.json({ message, importId }, { status: 400 });
  }

  try {
    if (parsed.detectedCountries.length > 0) {
      await ensureCountryAccess(parsed.detectedCountries, viewer);
      if (viewer.role === "admin") {
        ensureUploadScopeCountries(parsed.detectedCountries, uploadScope);
      }
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : "Country access denied.";
    await failImport(message, parsed.warnings);
    return NextResponse.json({ message, importId }, { status: 403 });
  }

  const mergedRows = mergeIncomingRows(parsed.rows);
  const parsedDocumentBrands = extractDocumentBrands(mergedRows, parsed.documentBrands);
  if (parsed.sentimentRows > 0) {
    parsed.warnings.push("Sentiment action rows were detected but are ignored in Supabase mode.");
  }
  const rpcPayload = mergedRows.map((row) => ({
    sent_email_name: row.sent_email_name,
    primary_country_code: row.primary_country_code,
    approved_document_name: row.approved_document_name,
    last_open: row.last_open,
    opened: row.opened,
    total_opens: row.total_opens,
    last_click: row.last_click,
    clicked: row.clicked,
    total_clicks: row.total_clicks,
    sent_date: row.sent_date,
  }));

  const { data: mergeData, error: mergeError } = await supabase.rpc("merge_raw_email_events", {
    p_import_id: importId,
    p_rows: rpcPayload,
  });

  if (mergeError) {
    await failImport(mergeError.message, parsed.warnings);
    return NextResponse.json({ message: mergeError.message, importId }, { status: 500 });
  }

  if (Object.keys(parsedDocumentBrands).length > 0) {
    try {
      const adminClient = createAdminClient();
      const metadataUpserts = Object.entries(parsedDocumentBrands).map(([approvedDocumentName, brand]) => ({
        approved_document_name: approvedDocumentName,
        brand,
      }));

      const { error: metadataError } = await adminClient
        .from("email_metadata")
        .upsert(metadataUpserts, { onConflict: "approved_document_name" });

      if (metadataError) {
        parsed.warnings.push(`Brand metadata sync warning: ${metadataError.message}`);
      }
    } catch (error) {
      const message = error instanceof Error ? error.message : "Unknown metadata sync error";
      parsed.warnings.push(`Brand metadata sync skipped: ${message}`);
    }
  }

  const counts = (Array.isArray(mergeData) ? mergeData[0] : mergeData) as
    | { inserted_rows?: number; updated_rows?: number; failed_rows?: number }
    | null;
  const insertedRows = Number(counts?.inserted_rows ?? 0);
  const updatedRows = Number(counts?.updated_rows ?? 0);
  const mergeFailedRows = Number(counts?.failed_rows ?? 0);
  const failedRows = mergeFailedRows + skippedRows;

  const { error: importUpdateError } = await supabase
    .from("imports")
    .update({
      status: "completed",
      storage_path: storagePath,
      total_rows: parsed.rows.length,
      inserted_rows: insertedRows,
      updated_rows: updatedRows,
      failed_rows: failedRows,
      detected_country_code:
        parsed.detectedCountries.length === 0
          ? null
          : parsed.detectedCountries.length === 1
            ? parsed.detectedCountries[0]
            : "MULTI",
      error_summary: compressSummary(parsed.warnings, parsed.errors),
    })
    .eq("id", importId);

  if (importUpdateError) {
    return NextResponse.json({ message: importUpdateError.message, importId }, { status: 500 });
  }

  return NextResponse.json({
    importId,
    insertedRows,
    updatedRows,
    failedRows,
    warnings: parsed.warnings,
  });
}
