import Button from "@/components/Low/Button";
import Checkbox from "@/components/Low/Checkbox";
import { Type, User } from "@/interfaces/user";
import { USER_TYPE_LABELS } from "@/resources/user";
import axios from "axios";
import clsx from "clsx";
import { capitalize, uniqBy } from "lodash";
import moment from "moment";
import { useEffect, useState } from "react";
import ReactDatePicker from "react-datepicker";
import { toast } from "react-toastify";
import ShortUniqueId from "short-unique-id";
import { useFilePicker } from "use-file-picker";
import readXlsxFile from "read-excel-file";
import Modal from "@/components/Modal";
import { checkAccess, getTypesOfUser } from "@/utils/permissions";
import { PermissionType } from "@/interfaces/permissions";
import { EntityWithRoles } from "@/interfaces/entity";
import Select from "@/components/Low/Select";
import CodeGenImportSummary, {
  ExcelCodegenDuplicatesMap,
} from "@/components/ImportSummaries/Codegen";
import { FaFileDownload } from "react-icons/fa";
import { IoInformationCircleOutline } from "react-icons/io5";
import { HiOutlineDocumentText } from "react-icons/hi";
import CodegenTable from "@/components/Tables/CodeGenTable";

const EMAIL_REGEX = new RegExp(
  /^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/
);

const USER_TYPE_PERMISSIONS: {
  [key in Type]: { perm: PermissionType | undefined; list: Type[] };
} = {
  student: {
    perm: "createCodeStudent",
    list: [],
  },
  teacher: {
    perm: "createCodeTeacher",
    list: [],
  },
  agent: {
    perm: "createCodeCountryManager",
    list: ["student", "teacher", "corporate", "mastercorporate"],
  },
  corporate: {
    perm: "createCodeCorporate",
    list: ["student", "teacher"],
  },
  mastercorporate: {
    perm: undefined,
    list: ["student", "teacher", "corporate"],
  },
  admin: {
    perm: "createCodeAdmin",
    list: [
      "student",
      "teacher",
      "agent",
      "corporate",
      "admin",
      "mastercorporate",
    ],
  },
  developer: {
    perm: undefined,
    list: [
      "student",
      "teacher",
      "agent",
      "corporate",
      "admin",
      "developer",
      "mastercorporate",
    ],
  },
};

interface Props {
  user: User;
  users: User[];
  permissions: PermissionType[];
  entities: EntityWithRoles[];
  onFinish: () => void;
}

export default function BatchCodeGenerator({
  user,
  users,
  entities = [],
  permissions,
  onFinish,
}: Props) {
  const [infos, setInfos] = useState<
    { email: string; name: string; passport_id: string }[]
  >([]);
  const [isLoading, setIsLoading] = useState(false);
  const [expiryDate, setExpiryDate] = useState<Date | null>(
    user?.subscriptionExpirationDate
      ? moment(user.subscriptionExpirationDate).toDate()
      : null
  );
  const [isExpiryDateEnabled, setIsExpiryDateEnabled] = useState(true);
  const [type, setType] = useState<Type>("student");
  const [showHelp, setShowHelp] = useState(false);
  const [entity, setEntity] = useState((entities || [])[0]?.id || undefined);
  const [parsedExcel, setParsedExcel] = useState<{
    rows?: any[];
    errors?: any[];
  }>({ rows: undefined, errors: undefined });
  const [duplicatedRows, setDuplicatedRows] = useState<{
    duplicates: ExcelCodegenDuplicatesMap;
    count: number;
  }>();

  const { openFilePicker, filesContent, clear } = useFilePicker({
    accept: ".xlsx",
    multiple: false,
    readAs: "ArrayBuffer",
  });

  useEffect(() => {
    if (!isExpiryDateEnabled) setExpiryDate(null);
  }, [isExpiryDateEnabled]);

  const schema = {
    "First Name": {
      prop: "firstName",
      type: String,
      required: true,
      validate: (value: string) => {
        if (!value || value.trim() === "") {
          throw new Error("First Name cannot be empty");
        }
        return true;
      },
    },
    "Last Name": {
      prop: "lastName",
      type: String,
      required: true,
      validate: (value: string) => {
        if (!value || value.trim() === "") {
          throw new Error("Last Name cannot be empty");
        }
        return true;
      },
    },
    "Passport/National ID": {
      prop: "passport_id",
      type: String,
      required: true,
      validate: (value: string) => {
        if (!value || value.trim() === "") {
          throw new Error("Passport/National ID cannot be empty");
        }
        return true;
      },
    },
    "E-mail": {
      prop: "email",
      required: true,
      type: (value: any) => {
        if (!value || value.trim() === "") {
          throw new Error("Email cannot be empty");
        }
        if (!EMAIL_REGEX.test(value.trim())) {
          throw new Error("Invalid Email");
        }
        return value;
      },
    },
  };

  useEffect(() => {
    if (filesContent.length > 0) {
      const file = filesContent[0];
      readXlsxFile(file.content, { schema, ignoreEmptyRows: false }).then(
        (data) => {
          setParsedExcel(data);
        }
      );
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filesContent]);

  useEffect(() => {
    if (parsedExcel.rows) {
      const duplicates: ExcelCodegenDuplicatesMap = {
        email: new Map(),
        passport_id: new Map(),
      };
      const duplicateValues = new Set<string>();
      const duplicateRowIndices = new Set<number>();

      const errorRowIndices = new Set(
        parsedExcel.errors?.map((error) => error.row) || []
      );

      parsedExcel.rows.forEach((row, index) => {
        if (!errorRowIndices.has(index + 2)) {
          (
            Object.keys(duplicates) as Array<keyof ExcelCodegenDuplicatesMap>
          ).forEach((field) => {
            if (row !== null) {
              const value = row[field];
              if (value) {
                if (!duplicates[field].has(value)) {
                  duplicates[field].set(value, [index + 2]);
                } else {
                  const existingRows = duplicates[field].get(value);
                  if (existingRows) {
                    existingRows.push(index + 2);
                    duplicateValues.add(value);
                    existingRows.forEach((rowNum) =>
                      duplicateRowIndices.add(rowNum)
                    );
                  }
                }
              }
            }
          });
        }
      });

      const info = parsedExcel.rows
        .map((row, index) => {
          if (
            errorRowIndices.has(index + 2) ||
            duplicateRowIndices.has(index + 2) ||
            row === null
          ) {
            return undefined;
          }
          const {
            firstName,
            lastName,
            studentID,
            passport_id,
            email,
            phone,
            group,
            country,
          } = row;
          if (!email || !EMAIL_REGEX.test(email.toString().trim())) {
            return undefined;
          }

          return {
            email: email.toString().trim().toLowerCase(),
            name: `${firstName ?? ""} ${lastName ?? ""}`.trim(),
            passport_id: passport_id?.toString().trim() || undefined,
          };
        })
        .filter((x) => !!x) as typeof infos;

      setInfos(info);
    }
  }, [entity, parsedExcel, type]);

  const generateAndInvite = async () => {
    const newUsers = infos.filter(
      (x) => !users.map((u) => u.email).includes(x.email)
    );
    const existingUsers = infos
      .filter((x) => users.map((u) => u.email).includes(x.email))
      .map((i) => users.find((u) => u.email === i.email))
      .filter((x) => !!x && x.type === "student") as User[];

    const newUsersSentence =
      newUsers.length > 0 ? `generate ${newUsers.length} code(s)` : undefined;
    const existingUsersSentence =
      existingUsers.length > 0
        ? `invite ${existingUsers.length} registered student(s)`
        : undefined;
    if (
      !confirm(
        `You are about to ${[newUsersSentence, existingUsersSentence]
          .filter((x) => !!x)
          .join(" and ")}, are you sure you want to continue?`
      )
    )
      return;

    setIsLoading(true);
    Promise.all(
      existingUsers.map(
        async (u) =>
          await axios.post(`/api/invites`, { to: u.id, from: user.id })
      )
    )
      .then(() =>
        toast.success(
          `Successfully invited ${existingUsers.length} registered student(s)!`
        )
      )
      .finally(() => {
        if (newUsers.length === 0) setIsLoading(false);
      });

    if (newUsers.length > 0) generateCode(type, newUsers);
    setInfos([]);
  };

  const generateCode = (type: Type, informations: typeof infos) => {
    const uid = new ShortUniqueId();
    const codes = informations.map(() => uid.randomUUID(6));

    setIsLoading(true);
    axios
      .post<{ ok: boolean; valid?: number; reason?: string }>("/api/code", {
        type,
        codes,
        infos: informations.map((info, index) => ({
          ...info,
          code: codes[index],
        })),
        expiryDate,
        entity,
      })
      .then(({ data, status }) => {
        if (data.ok) {
          toast.success(
            `Successfully generated${
              data.valid ? ` ${data.valid}/${informations.length}` : ""
            } ${capitalize(type)} codes and they have been notified by e-mail!`,
            { toastId: "success" }
          );

          onFinish();
          return;
        }

        if (status === 403) {
          toast.error(data.reason, { toastId: "forbidden" });
        }
      })
      .catch(({ response: { status, data } }) => {
        if (status === 403) {
          toast.error(data.reason, { toastId: "forbidden" });
          return;
        }

        toast.error(`Something went wrong, please try again later!`, {
          toastId: "error",
        });
      })
      .finally(() => {
        setIsLoading(false);
        return clear();
      });
  };

  const handleTemplateDownload = () => {
    const fileName = "BatchCodeTemplate.xlsx";
    const url = `https://firebasestorage.googleapis.com/v0/b/encoach-staging.appspot.com/o/import_templates%2F${fileName}?alt=media&token=b771a535-bf95-4060-889c-a086df65d480`;

    const link = document.createElement("a");
    link.href = url;

    link.download = fileName;

    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  return (
    <>
      <Modal isOpen={showHelp} onClose={() => setShowHelp(false)}>
        <>
          <div className="flex font-bold text-xl justify-center text-gray-700">
            <span>Excel File Format</span>
          </div>
          <div className="mt-4 flex flex-col gap-4">
            <div className="flex flex-col gap-3 bg-gray-100 rounded-lg p-4">
              <div className="flex items-center gap-2">
                <HiOutlineDocumentText
                  className={`w-5 h-5 text-mti-purple-light`}
                />
                <h2 className="text-lg font-semibold">
                  The uploaded document must:
                </h2>
              </div>
              <ul className="flex flex-col pl-10 gap-2">
                <li className="text-gray-700 list-disc">
                  be an Excel .xlsx document.
                </li>
                <li className="text-gray-700 list-disc">
                  only have a single spreadsheet with the following{" "}
                  <b>exact same name</b> columns:
                  <div className="py-4 pr-4">
                    <table className="w-full bg-white">
                      <thead>
                        <tr>
                          <th className="border border-neutral-200 px-2 py-1">
                            First Name
                          </th>
                          <th className="border border-neutral-200 px-2 py-1">
                            Last Name
                          </th>
                          <th className="border border-neutral-200 px-2 py-1">
                            Passport/National ID
                          </th>
                          <th className="border border-neutral-200 px-2 py-1">
                            E-mail
                          </th>
                        </tr>
                      </thead>
                    </table>
                  </div>
                </li>
              </ul>
            </div>
            <div className="flex flex-col gap-3 bg-gray-100 rounded-lg p-4">
              <div className="flex items-center gap-2">
                <IoInformationCircleOutline
                  className={`w-5 h-5 text-mti-purple-light`}
                />
                <h2 className="text-lg font-semibold">Note that:</h2>
              </div>
              <ul className="flex flex-col pl-10 gap-2">
                <li className="text-gray-700 list-disc">
                  all incorrect e-mails will be ignored.
                </li>
                <li className="text-gray-700 list-disc">
                  all already registered e-mails will be ignored.
                </li>
                <li className="text-gray-700 list-disc">
                  all rows which contain duplicate values in the columns:
                  &quot;Passport/National ID&quot;, &quot;E-mail&quot;, will be
                  ignored.
                </li>
                <li className="text-gray-700 list-disc">
                  all of the e-mails in the file will receive an e-mail to join
                  EnCoach with the role selected below.
                </li>
              </ul>
            </div>
            <div className="bg-gray-100 rounded-lg p-4">
              <p className="text-gray-600">
                {`The downloadable template is an example of a file that can be imported. Your document doesn't need to be a carbon copy of the template - it can have different styling but it must adhere to the previous requirements.`}
              </p>
            </div>
            <div className="w-full flex justify-between mt-6 gap-8">
              <Button
                color="purple"
                onClick={() => setShowHelp(false)}
                variant="outline"
                className="self-end w-full bg-white"
              >
                Close
              </Button>

              <Button
                color="purple"
                onClick={handleTemplateDownload}
                variant="solid"
                className="self-end w-full"
              >
                <div className="flex items-center gap-2">
                  <FaFileDownload size={24} />
                  Download Template
                </div>
              </Button>
            </div>
          </div>
        </>
      </Modal>
      <div className="border-mti-gray-platinum flex flex-col gap-4 rounded-xl border p-4">
        <div className="flex items-end justify-between">
          <label className="text-mti-gray-dim text-base font-normal">
            Choose an Excel file
          </label>
          <button
            onClick={() => setShowHelp(true)}
            className="tooltip cursor-pointer p-1.5 hover:bg-gray-200 rounded-full transition-colors duration-200"
            data-tip="Excel File Format"
          >
            <IoInformationCircleOutline size={24} />
          </button>
        </div>
        <Button
          onClick={openFilePicker}
          isLoading={isLoading}
          disabled={isLoading}
        >
          {filesContent.length > 0 ? filesContent[0].name : "Choose a file"}
        </Button>
        {user &&
          checkAccess(user, [
            "developer",
            "admin",
            "corporate",
            "mastercorporate",
          ]) && (
            <>
              <div className="-md:flex-row -md:items-center flex justify-between gap-2 md:flex-col 2xl:flex-row 2xl:items-center">
                <label className="text-mti-gray-dim text-base font-normal">
                  Expiry Date
                </label>
                <Checkbox
                  isChecked={isExpiryDateEnabled}
                  onChange={setIsExpiryDateEnabled}
                  disabled={!!user.subscriptionExpirationDate}
                >
                  Enabled
                </Checkbox>
              </div>
              {isExpiryDateEnabled && (
                <ReactDatePicker
                  className={clsx(
                    "flex min-h-[70px] w-full cursor-pointer justify-center rounded-full border p-6 text-sm font-normal focus:outline-none",
                    "hover:border-mti-purple tooltip",
                    "transition duration-300 ease-in-out"
                  )}
                  filterDate={(date) =>
                    moment(date).isAfter(new Date()) &&
                    (user.subscriptionExpirationDate
                      ? moment(date).isBefore(user.subscriptionExpirationDate)
                      : true)
                  }
                  dateFormat="dd/MM/yyyy"
                  selected={expiryDate}
                  onChange={(date) => setExpiryDate(date)}
                />
              )}
            </>
          )}
        <div className={clsx("flex flex-col gap-4")}>
          <label className="font-normal text-base text-mti-gray-dim">
            Entity
          </label>
          <Select
            defaultValue={{
              value: (entities || [])[0]?.id,
              label: (entities || [])[0]?.label,
            }}
            options={entities.map((e) => ({ value: e.id, label: e.label }))}
            onChange={(e) => setEntity(e?.value || undefined)}
            isClearable={checkAccess(user, ["admin", "developer"])}
          />
        </div>
        <label className="text-mti-gray-dim text-base font-normal">
          Select the type of user they should be
        </label>
        {user && (
          <select
            defaultValue="student"
            onChange={(e) => setType(e.target.value as typeof user.type)}
            className="flex min-h-[70px] w-full min-w-[350px] cursor-pointer justify-center rounded-full border bg-white p-6 text-sm font-normal focus:outline-none"
          >
            {Object.keys(USER_TYPE_LABELS).reduce((acc, x) => {
              const { list, perm } = USER_TYPE_PERMISSIONS[x as Type];
              if (checkAccess(user, getTypesOfUser(list), permissions, perm))
                acc.push(
                  <option key={type} value={type}>
                    {USER_TYPE_LABELS[type as keyof typeof USER_TYPE_LABELS]}
                  </option>
                );
              return acc;
            }, [] as JSX.Element[])}
          </select>
        )}
        {infos.length > 0 && (
          <CodeGenImportSummary
            infos={infos}
            parsedExcel={parsedExcel}
            duplicateRows={duplicatedRows}
          />
        )}
        {infos.length !== 0 && (
          <div className="flex w-full flex-col gap-4">
            <span className="text-mti-gray-dim text-base font-normal">
              Codes will be sent to:
            </span>
            <CodegenTable infos={infos} />
          </div>
        )}
        {checkAccess(
          user,
          ["developer", "admin", "corporate", "mastercorporate"],
          permissions,
          "createCodes"
        ) && (
          <Button
            onClick={generateAndInvite}
            disabled={
              infos.length === 0 || (isExpiryDateEnabled ? !expiryDate : false)
            }
          >
            Generate & Send
          </Button>
        )}
      </div>
    </>
  );
}
