import ClearIcon from "@mui/icons-material/Clear";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import FileDownloadIcon from "@mui/icons-material/FileDownload";
import SaveIcon from "@mui/icons-material/Save";
import {
  Button,
  Grid,
  ListItemAvatar,
  ListItemButton,
  ListItemText,
  Stack,
} from "@mui/material";
import * as EmailValidator from "email-validator";
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Couple } from "../../api/CoupleApi";
import { Member, MemberApi, fmtDate } from "../../api/MemberApi";
import { ServerApi } from "../../api/ServerApi";
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
import { AsyncWidget } from "../../widgets/AsyncWidget";
import { useFamily } from "../../widgets/BaseFamilyRoute";
import { ConfirmLeaveWithoutSaveDialog } from "../../widgets/ConfirmLeaveWithoutSaveDialog";
import { CouplePhoto } from "../../widgets/CouplePhoto";
import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
import { MemberItem } from "../../widgets/MemberItem";
import { MemberPhoto } from "../../widgets/MemberPhoto";
import { PropertiesBox } from "../../widgets/PropertiesBox";
import { RouterLink } from "../../widgets/RouterLink";
import { DateInput } from "../../widgets/forms/DateInput";
import { MemberInput } from "../../widgets/forms/MemberInput";
import { PropCheckbox } from "../../widgets/forms/PropCheckbox";
import { PropEdit } from "../../widgets/forms/PropEdit";
import { PropSelect } from "../../widgets/forms/PropSelect";
import { SexSelection } from "../../widgets/forms/SexSelection";
import { UploadPhotoButton } from "../../widgets/forms/UploadPhotoButton";
import { useQuery } from "../../hooks/useQuery";
import { mdiFamilyTree } from "@mdi/js";
import Icon from "@mdi/react";
import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";

/**
 * Create a new member route
 */
export function FamilyCreateMemberRoute(): React.ReactElement {
  const alert = useAlert();
  const snackbar = useSnackbar();

  const [shouldQuit, setShouldQuit] = React.useState(false);
  const n = useNavigate();
  const family = useFamily();

  const parameters = useQuery();
  const mother = Number(parameters.get("mother"));
  const father = Number(parameters.get("father"));

  const create = async (m: Member) => {
    try {
      const r = await MemberApi.Create(m);

      await family.reloadMembersList();

      setShouldQuit(true);
      n(family.family.URL(`member/${r.id}`));
      snackbar(`La fiche pour ${r.fullName} a été créée avec succès !`);
    } catch (e) {
      console.error(e);
      alert("Echec de la création de la personne !");
    }
  };

  const cancel = () => {
    setShouldQuit(true);
    n(family.family.URL("members"));
  };

  const member = Member.New(family.family.family_id);
  if (mother) member.mother = mother;
  if (father) member.father = father;

  return (
    <MemberPage
      member={member}
      creating={true}
      editing={true}
      onCancel={cancel}
      onSave={create}
      shouldAllowLeaving={shouldQuit}
    />
  );
}

/**
 * Get existing member route
 */
export function FamilyMemberRoute(): React.ReactElement {
  const count = React.useRef(1);

  const n = useNavigate();
  const alert = useAlert();
  const confirm = useConfirm();
  const snackbar = useSnackbar();

  const family = useFamily();
  const { memberId } = useParams();

  const [member, setMember] = React.useState<Member>();
  const load = async () => {
    setMember(await MemberApi.GetSingle(family.familyId, Number(memberId)));
  };

  const forceReload = async () => {
    count.current += 1;
    setMember(undefined);

    await family.reloadMembersList();
  };

  const deleteMember = async () => {
    try {
      if (
        !(await confirm(
          "Voulez-vous vraiment supprimer cette fiche membre ? L'opération n'est pas réversible !"
        ))
      )
        return;

      await MemberApi.Delete(member!);

      snackbar("La fiche de membre a été supprimée avec succès !");
      n(family.family.URL("members"));

      await family.reloadMembersList();
    } catch (e) {
      console.error(e);
      alert("Échec de la suppression du membre !");
    }
  };

  return (
    <AsyncWidget
      loadKey={`${memberId}-${count.current}`}
      load={load}
      ready={member !== undefined}
      errMsg="Echec du chargement des informations du membre"
      build={() => (
        <MemberPage
          member={member!}
          children={family.members.children(member!.id)}
          siblings={family.members.siblings(member!.id)}
          couples={family.couples.getAllOf(member!)}
          creating={false}
          editing={false}
          onrequestOpenTree={() => n(family.family.familyTreeURL(member!))}
          onRequestDelete={deleteMember}
          onRequestEdit={() =>
            n(family.family.URL(`member/${member!.id}/edit`))
          }
          onForceReload={forceReload}
        />
      )}
    />
  );
}

/**
 * Edit existing member route
 */
export function FamilyEditMemberRoute(): React.ReactElement {
  const n = useNavigate();
  const { memberId } = useParams();

  const alert = useAlert();
  const snackbar = useSnackbar();

  const [shouldQuit, setShouldQuit] = React.useState(false);

  const family = useFamily();

  const [member, setMember] = React.useState<Member>();
  const load = async () => {
    setMember(await MemberApi.GetSingle(family.familyId, Number(memberId)));
  };

  const cancel = () => {
    setShouldQuit(true);
    n(family.family.memberURL(member!));
    //n(-1);
  };

  const save = async (m: Member) => {
    try {
      await MemberApi.Update(m);

      snackbar("Les informations du membre ont été mises à jour avec succès !");

      await family.reloadMembersList();

      setShouldQuit(true);
      n(family.family.URL(`member/${member!.id}`));
    } catch (e) {
      console.error(e);
      alert("Échec de la mise à jour des informations du membre !");
    }
  };

  return (
    <AsyncWidget
      loadKey={memberId}
      load={load}
      errMsg="Échec du chargement des informations du membre"
      build={() => (
        <MemberPage
          member={member!}
          creating={false}
          editing={true}
          onCancel={cancel}
          onSave={save}
          shouldAllowLeaving={shouldQuit}
        />
      )}
    />
  );
}

export function MemberPage(p: {
  member: Member;
  editing: boolean;
  creating: boolean;
  shouldAllowLeaving?: boolean;
  children?: Member[];
  siblings?: Member[];
  couples?: Couple[];
  onCancel?: () => void;
  onSave?: (m: Member) => Promise<void>;
  onRequestEdit?: () => void;
  onRequestDelete?: () => void;
  onForceReload?: () => void;
  onrequestOpenTree?: () => void;
}): React.ReactElement {
  const confirm = useConfirm();
  const snackbar = useSnackbar();
  const loadingMessage = useLoadingMessage();

  const family = useFamily();

  const [changed, setChanged] = React.useState(false);
  const [member, setMember] = React.useState(
    new Member(structuredClone(p.member))
  );

  const updatedMember = () => {
    setChanged(true);
    setMember(new Member(structuredClone(member)));
  };

  const save = async () => {
    loadingMessage.show(
      "Enregistrement des informations du membre en cours..."
    );
    await p.onSave!(member);
    loadingMessage.hide();
  };

  const cancel = async () => {
    if (
      changed &&
      !(await confirm(
        "Voulez-vous vraiment retirer les modifications apportées ? Celles-ci seront perdues !"
      ))
    )
      return;

    p.onCancel!();
  };

  const uploadNewPhoto = async (b: Blob) => {
    await MemberApi.SetMemberPhoto(member, b);
    snackbar("La photo du membre a été mise à jour avec succès !");
    p.onForceReload?.();
  };

  const deletePhoto = async () => {
    try {
      if (!(await confirm("Voulez-vous supprimer cette photo ?"))) return;

      await MemberApi.RemoveMemberPhoto(member);

      snackbar("La photo du membre a été supprimée avec succès !");
      p.onForceReload?.();
    } catch (e) {
      console.error(e);
      alert("Échec de la suppresion de la photo !");
    }
  };

  return (
    <div style={{ maxWidth: "2000px", margin: "auto" }}>
      <ConfirmLeaveWithoutSaveDialog
        shouldBlock={changed && p.shouldAllowLeaving !== true}
      />
      <div
        style={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
        }}
      >
        <FamilyPageTitle
          title={
            (p.editing
              ? p.creating
                ? "Création"
                : "Édition"
              : "Visualisation") + " d'une fiche de membre"
          }
        />
        <Stack direction="row" spacing={1}>
          {/* Family tree button */}
          {p.onrequestOpenTree && (
            <Button
              variant="outlined"
              startIcon={<Icon path={mdiFamilyTree} size={1} />}
              onClick={p.onrequestOpenTree}
              size="large"
            >
              Arbre
            </Button>
          )}
          {/* Edit button */}
          {p.onRequestEdit && (
            <Button
              variant="outlined"
              startIcon={<EditIcon />}
              onClick={p.onRequestEdit}
              size="large"
            >
              Editer
            </Button>
          )}

          {/* Delete button */}
          {p.onRequestDelete && (
            <Button
              variant="outlined"
              startIcon={<DeleteIcon />}
              onClick={p.onRequestDelete}
              size="large"
              color="error"
            >
              Supprimer
            </Button>
          )}

          {/* Save button */}
          {p.editing && changed && (
            <Button
              variant={"contained"}
              startIcon={<SaveIcon />}
              onClick={save}
              size="large"
            >
              Enregistrer
            </Button>
          )}

          {/* Cancel button */}
          {p.editing && (
            <Button
              variant="outlined"
              startIcon={<ClearIcon />}
              onClick={cancel}
              size="small"
            >
              Annuler les modifications
            </Button>
          )}
        </Stack>
      </div>

      <Grid container spacing={2}>
        {/* General info */}
        <Grid item sm={12} md={6}>
          <PropertiesBox title="Informations générales">
            {/* Sex */}
            <SexSelection
              readonly={!p.editing}
              current={member.sex}
              onChange={(v) => {
                member.sex = v;
                updatedMember();
              }}
            />

            {/* First name */}
            <PropEdit
              label="Prénom"
              editable={p.editing}
              value={member.first_name}
              onValueChange={(v) => {
                member.first_name = v;
                updatedMember();
              }}
              size={ServerApi.Config.constraints.member_first_name}
            />

            {/* Last name */}
            <PropEdit
              label="Nom"
              editable={p.editing}
              value={member.last_name}
              onValueChange={(v) => {
                member.last_name = v;
                updatedMember();
              }}
              size={ServerApi.Config.constraints.member_last_name}
            />

            {/* Birth last name */}
            <PropEdit
              label="Nom de naissance"
              editable={p.editing}
              value={member.birth_last_name}
              onValueChange={(v) => {
                member.birth_last_name = v;
                updatedMember();
              }}
              size={ServerApi.Config.constraints.member_birth_last_name}
            />

            {/* Birth day */}
            <DateInput
              label="Date de naissance"
              editable={p.editing}
              id="dob"
              value={member.dateOfBirth}
              onValueChange={(d) => {
                member.birth_year = d.year;
                member.birth_month = d.month;
                member.birth_day = d.day;
                updatedMember();
              }}
            />

            {/* Is dead */}
            <PropCheckbox
              checked={member.dead}
              editable={p.editing}
              label={member.sex === "F" ? "Décédée" : "Décédé"}
              onValueChange={(v) => {
                member.dead = v;
                updatedMember();
              }}
            />

            {/* Death day */}
            <DateInput
              label="Date de décès"
              editable={p.editing}
              id="dod"
              value={member.dateOfDeath}
              onValueChange={(d) => {
                member.death_year = d.year;
                member.death_month = d.month;
                member.death_day = d.day;
                updatedMember();
              }}
            />

            {/* Father */}
            <br />
            <MemberInput
              editable={p.editing}
              label="Père"
              onValueChange={(m) => {
                member.father = m;
                updatedMember();
              }}
              filter={(m) =>
                (m.sex === "M" || m.sex === undefined) && m.id !== member.id
              }
              current={member.father}
            />

            {/* Mother */}
            <br />
            <MemberInput
              editable={p.editing}
              label="Mère"
              onValueChange={(m) => {
                member.mother = m;
                updatedMember();
              }}
              filter={(m) =>
                (m.sex === "F" || m.sex === undefined) && m.id !== member.id
              }
              current={member.mother}
            />
          </PropertiesBox>
        </Grid>

        {/* Photo */}
        <Grid item sm={12} md={6}>
          <PropertiesBox title="Photo">
            <div style={{ textAlign: "center" }}>
              <MemberPhoto member={member} width={150} />
              <br />
              {p.editing ? (
                <p>
                  Veuillez enregistrer / annuler les modifications apportées à
                  la fiche avant de changer la photo du membre.
                </p>
              ) : (
                <>
                  <UploadPhotoButton
                    label={member.hasPhoto ? "Remplacer" : "Ajouter"}
                    onPhotoSelected={uploadNewPhoto}
                    aspect={4 / 5}
                  />{" "}
                  {member.hasPhoto && (
                    <RouterLink to={member.photoURL!} target="_blank">
                      <Button
                        variant="outlined"
                        startIcon={<FileDownloadIcon />}
                      >
                        Télécharger
                      </Button>
                    </RouterLink>
                  )}{" "}
                  {member.hasPhoto && (
                    <Button
                      variant="outlined"
                      startIcon={<DeleteIcon />}
                      color="error"
                      onClick={deletePhoto}
                    >
                      Supprimer
                    </Button>
                  )}
                </>
              )}{" "}
            </div>
          </PropertiesBox>
        </Grid>

        {/* Contact */}
        {(p.editing || member.hasContactInfo) && (
          <Grid item sm={12} md={6}>
            <PropertiesBox title="Contact">
              {/* Email */}
              <PropEdit
                label="Adresse mail"
                editable={p.editing}
                value={member.email}
                onValueChange={(v) => {
                  member.email = v;
                  updatedMember();
                }}
                size={ServerApi.Config.constraints.member_email}
                checkValue={(e) => EmailValidator.validate(e)}
              />

              {/* Phone number */}
              <PropEdit
                label="Téléphone"
                editable={p.editing}
                value={member.phone}
                onValueChange={(v) => {
                  member.phone = v;
                  updatedMember();
                }}
                size={ServerApi.Config.constraints.member_phone}
              />

              {/* Country */}
              <PropSelect
                label="Pays"
                editing={p.editing}
                onValueChange={(o) => {
                  member.country = o;
                  updatedMember();
                }}
                value={member.country}
                options={ServerApi.Config.countries.map((c) => {
                  return { label: c.fr, value: c.code };
                })}
              />
              {/* Address */}
              <PropEdit
                label="Adresse"
                editable={p.editing}
                value={member.address}
                onValueChange={(v) => {
                  member.address = v;
                  updatedMember();
                }}
                size={ServerApi.Config.constraints.member_address}
              />

              {/* Postal code */}
              <PropEdit
                label="Code postal"
                editable={p.editing}
                value={member.postal_code}
                onValueChange={(v) => {
                  member.postal_code = v;
                  updatedMember();
                }}
                size={ServerApi.Config.constraints.member_postal_code}
              />

              {/* City */}
              <PropEdit
                label="Ville"
                editable={p.editing}
                value={member.city}
                onValueChange={(v) => {
                  member.city = v;
                  updatedMember();
                }}
                size={ServerApi.Config.constraints.member_city}
              />
            </PropertiesBox>
          </Grid>
        )}

        {/* Bio */}
        {(p.editing || member.hasNote) && (
          <Grid item sm={12} md={6}>
            <PropertiesBox title="Biographie">
              <PropEdit
                label="Biographie"
                editable={p.editing}
                multiline={true}
                minRows={5}
                maxRows={20}
                value={member.note}
                onValueChange={(v) => {
                  member.note = v;
                  updatedMember();
                }}
                size={ServerApi.Config.constraints.member_note}
              />
            </PropertiesBox>
          </Grid>
        )}

        {/* Couples */}
        {p.couples && (
          <Grid item sm={12} md={6}>
            <PropertiesBox title={member.sex === "F" ? "Époux" : "Épouse"}>
              {p.couples!.length === 0 ? (
                <>{member.sex === "F" ? "Aucun époux" : "Aucune épouse"}</>
              ) : (
                p.couples.map((c) => (
                  <CoupleItem key={c.id} currMemberId={member.id} couple={c} />
                ))
              )}

              <div style={{ display: "flex", justifyContent: "end" }}>
                <RouterLink
                  to={family.family.URL(
                    `couple/create?${member.sex === "F" ? "wife" : "husband"}=${
                      member.id
                    }`
                  )}
                >
                  <Button>Nouveau</Button>
                </RouterLink>
              </div>
            </PropertiesBox>
          </Grid>
        )}

        {/* Children */}
        {p.children && (
          <Grid item sm={12} md={6}>
            <PropertiesBox title="Enfants">
              {p.children.length === 0 ? (
                <>Aucun enfant</>
              ) : (
                p.children.map((c) => (
                  <RouterLink
                    key={c.id}
                    to={family.family.URL(`member/${c.id}`)}
                  >
                    <MemberItem member={c} />
                  </RouterLink>
                ))
              )}

              <div style={{ display: "flex", justifyContent: "end" }}>
                <RouterLink
                  to={family.family.URL(
                    `member/create?${
                      member.sex === "F" ? "mother" : "father"
                    }=${member.id}`
                  )}
                >
                  <Button>Nouveau</Button>
                </RouterLink>
              </div>
            </PropertiesBox>
          </Grid>
        )}

        {/* Siblings */}
        {p.siblings && (
          <Grid item sm={12} md={6}>
            <PropertiesBox title="Frères et sœurs">
              {p.siblings.length === 0 ? (
                <>Aucun frère ou sœur</>
              ) : (
                p.siblings.map((c) => (
                  <RouterLink
                    key={c.id}
                    to={family.family.URL(`member/${c.id}`)}
                  >
                    <MemberItem member={c} />
                  </RouterLink>
                ))
              )}

              {(member.mother || member.father) && (
                <div style={{ display: "flex", justifyContent: "end" }}>
                  <RouterLink
                    to={family.family.URL(
                      `member/create?mother=${member.mother}&father=${member.father}`
                    )}
                  >
                    <Button>Nouveau</Button>
                  </RouterLink>
                </div>
              )}
            </PropertiesBox>
          </Grid>
        )}
      </Grid>
    </div>
  );
}

function CoupleItem(p: {
  currMemberId: number;
  couple: Couple;
}): React.ReactElement {
  const n = useNavigate();

  const family = useFamily();

  const statusStr = ServerApi.Config.couples_states.find(
    (c) => c.code === p.couple.state
  )?.fr;

  const status = [];
  if (statusStr) status.push(statusStr);

  if (p.couple.dateOfWedding)
    status.push(`Mariage : ${fmtDate(p.couple.dateOfWedding)}`);

  if (p.couple.dateOfDivorce)
    status.push(`Divorce : ${fmtDate(p.couple.dateOfDivorce)}`);

  const otherSpouseID =
    p.couple.wife === p.currMemberId ? p.couple.husband : p.couple.wife;
  const otherSpouse = otherSpouseID
    ? family.members.get(otherSpouseID)
    : undefined;

  return (
    <ListItemButton onClick={() => n(family.family.coupleURL(p.couple))}>
      <ListItemAvatar>
        {p.couple.hasPhoto ? (
          <CouplePhoto couple={p.couple!} />
        ) : (
          <MemberPhoto member={otherSpouse} />
        )}
      </ListItemAvatar>
      <ListItemText
        primary={otherSpouse ? otherSpouse.fullName : "___ ___"}
        secondary={status.join(" - ")}
      ></ListItemText>
    </ListItemButton>
  );
}