"use client";

import { parse } from "csv-parse/sync";
import { ArrowUpFromLineIcon, FileUpIcon, PlusIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useRef, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
import { cn } from "@/lib/cn";
import { isStringMatch } from "@/lib/utils/helper";
import { isSafeIdentifier } from "@/lib/utils/safe-identifier";
import { createContactsFromCSVAction } from "@/modules/ee/contacts/actions";
import { CsvTable } from "@/modules/ee/contacts/components/csv-table";
import { UploadContactsAttributes } from "@/modules/ee/contacts/components/upload-contacts-attribute";
import { TContactCSVUploadResponse, ZContactCSVUploadResponse } from "@/modules/ee/contacts/types/contact";
import { Alert } from "@/modules/ui/components/alert";
import { Button } from "@/modules/ui/components/button";
import {
  Dialog,
  DialogBody,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/modules/ui/components/dialog";
import { StylingTabs } from "@/modules/ui/components/styling-tabs";

interface UploadContactsCSVButtonProps {
  environmentId: string;
  contactAttributeKeys: TContactAttributeKey[];
}

export const UploadContactsCSVButton = ({
  environmentId,
  contactAttributeKeys,
}: UploadContactsCSVButtonProps) => {
  const { t } = useTranslation();
  const router = useRouter();

  const errorContainerRef = useRef<HTMLDivElement | null>(null);
  const [open, setOpen] = useState(false);
  const [duplicateContactsAction, setDuplicateContactsAction] = useState<"skip" | "update" | "overwrite">(
    "skip"
  );
  const [csvResponse, setCSVResponse] = useState<TContactCSVUploadResponse>([]);
  const [attributeMap, setAttributeMap] = useState<Record<string, string>>({});
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);

  const processCSVFile = async (file: File) => {
    if (!file) return;

    // Check file type
    if (!file.type && !file.name.endsWith(".csv")) {
      setError(t("environments.contacts.upload_contacts_error_invalid_file_type"));
      return;
    }

    if (file.type && file.type !== "text/csv" && !file.type.includes("csv")) {
      setError(t("environments.contacts.upload_contacts_error_invalid_file_type"));
      return;
    }

    // Max file size check (800KB)
    const maxSizeInBytes = 800 * 1024;
    if (file.size > maxSizeInBytes) {
      setError(t("environments.contacts.upload_contacts_error_file_too_large"));
      return;
    }

    const reader = new FileReader();
    reader.onload = async (e) => {
      setError("");
      const csv = e.target?.result as string;

      try {
        const records = parse(csv, {
          columns: true, // Parse the header as column names
          skip_empty_lines: true, // Skip empty lines
        });

        const parsedRecords = ZContactCSVUploadResponse.safeParse(records);
        if (!parsedRecords.success) {
          console.error("Error parsing CSV:", parsedRecords.error);
          setError(parsedRecords.error.issues[0].message);
          return;
        }

        if (!parsedRecords.data.length) {
          setError(t("environments.contacts.upload_contacts_error_no_valid_contacts"));
          return;
        }

        setCSVResponse(parsedRecords.data);
      } catch (error) {
        console.error("Error parsing CSV:", error);
      }
    };

    reader.readAsText(file);
  };

  const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target?.files?.[0];
    if (file) {
      processCSVFile(file);
    }
  };

  const csvColumns = useMemo(() => {
    if (!csvResponse.length) {
      return [];
    }

    // Extract column names (headers) from the first row
    const headers = Object.keys(csvResponse[0]);

    return headers.map((header) => header.trim());
  }, [csvResponse]);

  // Filter columns to only show those that can be mapped (existing attributes or valid new keys)
  const validCsvColumns = useMemo(() => {
    return csvColumns.filter((column) => {
      // Check if column matches an existing attribute
      const matchesExisting = contactAttributeKeys.some((attrKey) =>
        isStringMatch(column, attrKey.name ?? attrKey.key)
      );
      // If it matches existing or is a valid safe identifier, include it
      return matchesExisting || isSafeIdentifier(column);
    });
  }, [csvColumns, contactAttributeKeys]);

  const resetState = (closeModal?: boolean) => {
    setCSVResponse([]);
    setDuplicateContactsAction("skip");
    setError("");
    setAttributeMap({});
    setLoading(false);

    if (closeModal) {
      setOpen(false);
    }
  };

  const handleUpload = async () => {
    if (!csvResponse.length) {
      return;
    }

    setLoading(true);
    setError("");

    const values = Object.values(attributeMap);

    if (new Set(values).size !== values.length) {
      const valueCount = values.reduce<Record<string, number>>((acc, value) => {
        acc[value] = (acc[value] || 0) + 1;
        return acc;
      }, {});

      const duplicateValues = Object.entries(valueCount)
        .filter(([_, count]) => count > 1)
        .map(([value, _]) => value);

      const duplicateAttributeKeys = Object.entries(attributeMap)
        .filter(([_, value]) => duplicateValues.includes(value))
        .map(([key, _]) => key);

      setError(
        t("environments.contacts.upload_contacts_error_duplicate_mappings", {
          attributes: duplicateAttributeKeys.join(", "),
        })
      );
      errorContainerRef.current?.scrollIntoView({ behavior: "smooth", block: "center" });
      setLoading(false);
      return;
    }

    const transformedCsvData = csvResponse.map((record) => {
      const newRecord: Record<string, string> = {};
      Object.entries(record).forEach(([key, value]) => {
        // if the key is in the attribute map, we wanna replace it
        if (attributeMap[key]) {
          const attrKeyId = attributeMap[key];
          const attrKey = contactAttributeKeys.find((attrKey) => attrKey.id === attrKeyId);

          if (attrKey) {
            newRecord[attrKey.key] = value;
          } else {
            newRecord[attrKeyId] = value;
          }
        } else {
          newRecord[key] = value;
        }
      });

      return newRecord;
    });

    const result = await createContactsFromCSVAction({
      csvData: transformedCsvData,
      duplicateContactsAction,
      attributeMap,
      environmentId,
    });

    if (result?.data) {
      if ("validationErrors" in result.data) {
        const { validationErrors } = result.data;
        const errorMessages = validationErrors.map((err) => {
          const sampleInvalid = err.invalidValues.slice(0, 3).join(", ");
          const additionalCount = err.invalidValues.length - 3;
          const suffix = additionalCount > 0 ? ` (${additionalCount.toString()} more)` : "";
          return t("environments.contacts.upload_contacts_error_attribute_type_mismatch", {
            key: err.key,
            dataType: err.dataType,
            values: `${sampleInvalid}${suffix}`,
          });
        });
        setError(errorMessages.join("\n"));
        setLoading(false);
        return;
      }

      setError("");
      toast.success(t("environments.contacts.upload_contacts_success"));
      resetState(true);

      router.refresh();
      return;
    }

    if (result?.serverError) {
      setError(result.serverError);
      setLoading(false);
      return;
    }

    if (result?.validationErrors) {
      const csvDataErrors = Array.isArray(result.validationErrors.csvData)
        ? result.validationErrors.csvData[0]?._errors?.[0]
        : result.validationErrors.csvData?._errors?.[0];

      if (csvDataErrors) {
        setError(csvDataErrors);
      } else {
        setError(t("environments.contacts.upload_contacts_error_generic"));
      }
      setLoading(false);
      return;
    }

    setLoading(false);
  };

  useEffect(() => {
    const matches: Record<string, string> = {};
    const invalidColumns: string[] = [];

    for (const columnName of csvColumns) {
      let matched = false;
      for (const attributeKey of contactAttributeKeys) {
        if (isStringMatch(columnName, attributeKey.name ?? attributeKey.key)) {
          matches[columnName] = attributeKey.id;
          matched = true;
          break;
        }
      }

      if (!matched) {
        // This column will become a new attribute - validate it's a safe identifier
        if (!isSafeIdentifier(columnName)) {
          invalidColumns.push(columnName);
        }
        matches[columnName] = columnName;
      }
    }

    setAttributeMap(matches);

    // Show error for invalid column names that would become new attributes
    if (invalidColumns.length > 0) {
      setError(
        t("environments.contacts.invalid_csv_column_names", {
          columns: invalidColumns.join(", "),
        })
      );
    }
  }, [contactAttributeKeys, csvColumns, t]);

  useEffect(() => {
    if (error && errorContainerRef.current) {
      // Small delay to ensure DOM has updated and the alert is visible
      setTimeout(() => {
        errorContainerRef.current?.scrollIntoView({ behavior: "smooth", block: "center" });
      }, 100);
    }
  }, [error]);

  // Function to download an example CSV
  const handleDownloadExampleCSV = () => {
    const exampleData = [
      {
        email: "user1@example.com",
        userId: "1001",
        first_name: "John",
        last_name: "Doe",
        age: "28",
        plan: "premium",
        signup_date: "2024-01-15",
      },
      {
        email: "user2@example.com",
        userId: "1002",
        first_name: "Jane",
        last_name: "Smith",
        age: "34",
        plan: "free",
        signup_date: "2024-02-20",
      },
      {
        email: "user3@example.com",
        userId: "1003",
        first_name: "Mark",
        last_name: "Jones",
        age: "45",
        plan: "enterprise",
        signup_date: "2023-11-08",
      },
      {
        email: "user4@example.com",
        userId: "1004",
        first_name: "Emily",
        last_name: "Brown",
        age: "22",
        plan: "premium",
        signup_date: "2024-03-01",
      },
      {
        email: "user5@example.com",
        userId: "1005",
        first_name: "David",
        last_name: "Wilson",
        age: "31",
        plan: "free",
        signup_date: "2024-01-28",
      },
    ];

    const headers = Object.keys(exampleData[0]);
    const csvRows = [
      headers.join(","),
      ...exampleData.map((row) => headers.map((h) => (row as Record<string, string>)[h]).join(",")),
    ];
    const csvString = csvRows.join("\n");
    const csvContent = "data:text/csv;charset=utf-8," + encodeURIComponent(csvString);

    const link = document.createElement("a");
    link.setAttribute("href", csvContent);
    link.setAttribute("download", "example.csv");
    document.body.appendChild(link); // Required for Firefox
    link.click();
    document.body.removeChild(link);
  };

  const handleDragOver = (e: React.DragEvent<HTMLLabelElement>) => {
    e.preventDefault();
    e.stopPropagation();

    // Only show copy cursor if file is CSV
    const items = Array.from(e.dataTransfer.items);
    const isCSV = items.some(
      (item) => item.type === "text/csv" || (item.type === "" && item.kind === "file") // For when type isn't available
    );

    e.dataTransfer.dropEffect = isCSV ? "copy" : "none";
  };

  const handleDrop = (e: React.DragEvent<HTMLLabelElement>) => {
    e.preventDefault();
    e.stopPropagation();

    const file = e.dataTransfer.files[0];
    if (file) {
      processCSVFile(file);
    }
  };

  return (
    <>
      <Button size="sm" onClick={() => setOpen(true)}>
        {t("common.upload")} CSV
        <PlusIcon />
      </Button>
      <Dialog
        open={open}
        onOpenChange={(newOpen) => {
          setOpen(newOpen);
          if (!newOpen) {
            setError("");
          }
        }}>
        <DialogContent
          disableCloseOnOutsideClick={true}
          unconstrained={true}
          style={{ scrollbarGutter: "stable" }}>
          <DialogHeader>
            <FileUpIcon />
            <DialogTitle>{t("common.upload")} CSV</DialogTitle>
            <DialogDescription>
              {t("environments.contacts.upload_contacts_modal_description")}
            </DialogDescription>
          </DialogHeader>

          <DialogBody unconstrained={false}>
            <div className="flex flex-col gap-4">
              {error ? (
                <div ref={errorContainerRef}>
                  <Alert variant="error" size="small">
                    {error}
                  </Alert>
                </div>
              ) : null}
              <div className="flex flex-col gap-2">
                <div className="no-scrollbar rounded-md border-2 border-dashed border-slate-300 bg-slate-50 p-4">
                  {!csvResponse.length ? (
                    <div>
                      <label
                        htmlFor="file"
                        className={cn(
                          "relative flex cursor-pointer flex-col items-center justify-center rounded-lg hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-700 dark:hover:border-slate-500 dark:hover:bg-slate-800"
                        )}
                        onDragOver={(e) => handleDragOver(e)}
                        onDrop={(e) => handleDrop(e)}>
                        <div className="flex flex-col items-center justify-center pb-6 pt-5">
                          <ArrowUpFromLineIcon className="h-6 text-slate-500" />
                          <p className={cn("mt-2 text-center text-sm text-slate-500")}>
                            <span className="font-semibold">{t("common.upload_input_description")}</span>
                          </p>
                          <input
                            type="file"
                            id={"file"}
                            name={"file"}
                            accept=".csv"
                            className="hidden"
                            onChange={handleFileUpload}
                          />
                        </div>
                      </label>
                    </div>
                  ) : (
                    <div className="flex flex-col items-center gap-4">
                      <h3 className="font-medium text-slate-500">
                        {t("environments.contacts.upload_contacts_modal_preview")}
                      </h3>
                      <div className="max-h-[300px] w-full overflow-auto rounded-md border border-slate-300">
                        <CsvTable data={[...csvResponse.slice(0, 11)]} />
                      </div>
                    </div>
                  )}
                </div>
                {!csvResponse.length && (
                  <div className="flex justify-start">
                    <Button onClick={handleDownloadExampleCSV} variant="secondary">
                      {t("environments.contacts.upload_contacts_modal_download_example_csv")}
                    </Button>
                  </div>
                )}
              </div>

              {csvResponse.length > 0 ? (
                <div className="flex flex-col">
                  <h3 className="font-medium text-slate-900">
                    {t("environments.contacts.upload_contacts_modal_attributes_title")}
                  </h3>
                  <p className="mb-2 text-slate-500">
                    {t("environments.contacts.upload_contacts_modal_attributes_description")}
                  </p>

                  <div className="grid grid-cols-[minmax(150px,1fr)_minmax(200px,2fr)] gap-x-4 gap-y-3">
                    <div className="font-medium text-slate-900">
                      {t("environments.contacts.upload_contacts_modal_csv_column_header")}
                    </div>
                    <div className="font-medium text-slate-900">
                      {t("environments.contacts.upload_contacts_modal_attribute_header")}
                    </div>

                    {validCsvColumns.map((column, index) => {
                      return (
                        <UploadContactsAttributes
                          key={index}
                          csvColumn={column}
                          attributeMap={attributeMap}
                          setAttributeMap={setAttributeMap}
                          contactAttributeKeys={contactAttributeKeys}
                        />
                      );
                    })}
                  </div>
                </div>
              ) : null}

              <div className="flex flex-col gap-2">
                <h3 className="font-medium text-slate-900">
                  {t("environments.contacts.upload_contacts_modal_duplicates_title")}
                </h3>
                <p className="text-slate-500">
                  {t("environments.contacts.upload_contacts_modal_duplicates_description")}
                </p>
                <StylingTabs
                  id="duplicate-contacts"
                  options={[
                    {
                      value: "skip",
                      label: t("environments.contacts.upload_contacts_modal_duplicates_skip_title"),
                    },
                    {
                      value: "update",
                      label: t("environments.contacts.upload_contacts_modal_duplicates_update_title"),
                    },
                    {
                      value: "overwrite",
                      label: t("environments.contacts.upload_contacts_modal_duplicates_overwrite_title"),
                    },
                  ]}
                  defaultSelected={duplicateContactsAction}
                  onChange={(value) => setDuplicateContactsAction(value)}
                  className="max-w-[400px]"
                  tabsContainerClassName="p-1 rounded-lg"
                />

                <div>
                  <p className="text-xs font-medium text-slate-500">
                    {duplicateContactsAction === "skip" &&
                      t("environments.contacts.upload_contacts_modal_duplicates_skip_description")}
                    {duplicateContactsAction === "update" &&
                      t("environments.contacts.upload_contacts_modal_duplicates_update_description")}
                    {duplicateContactsAction === "overwrite" &&
                      t("environments.contacts.upload_contacts_modal_duplicates_overwrite_description")}
                  </p>
                </div>
              </div>
            </div>
          </DialogBody>

          <DialogFooter>
            {csvResponse.length > 0 ? (
              <Button
                variant="secondary"
                onClick={() => {
                  resetState();
                }}>
                {t("environments.contacts.upload_contacts_modal_pick_different_file")}
              </Button>
            ) : null}

            <Button
              onClick={handleUpload}
              loading={loading}
              disabled={loading || !csvResponse.length || !!error}>
              {t("environments.contacts.upload_contacts_modal_upload_btn")}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
};
