import { HasChildren } from "@types";
import { ResultCode } from "api";
import {
  login as APILogin,
  logout as APILogout,
  changeContract as APIChangeContract,
  me,
} from "api/auth";
import { CustomCode } from "codes/custom";
import { PageID } from "codes/page";
import { UserRole } from "codes/user";
import useAPICall from "hooks/useAPICall";
import {
  createContext,
  memo,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from "react";
import { AUTH } from "routes/auth";

type Auth = {
  initialized: boolean;

  authenticated: boolean;

  role: UserRole;
  contractId: string | null;
  userId: string | null;
  name: string;
  custom: CustomCode[];
  customerName: string;

  isChangedContractId: boolean;

  login: (email: string, password: string) => Promise<boolean>;
  logout: VoidFunction;

  changeContractId: (contractId: string | null) => Promise<boolean>;

  checkRole: (role?: UserRole) => boolean;
  canAccess: (pageId: PageID) => boolean;
};
export const AuthContext = createContext<Auth>({
  initialized: false,

  authenticated: false,

  role: UserRole.NONE,
  contractId: null,
  userId: null,
  name: "",
  custom: [],
  customerName: "",

  isChangedContractId: false,

  login: async (email: string, password: string) => false,
  logout: () => {},
  changeContractId: async (contractId: string | null) => false,
  checkRole: (role?: UserRole) => false,
  canAccess: (pageId: PageID) => false,
});

type Props = HasChildren;
function AuthContextProvider({ children }: Props) {
  const [initialized, setInitialized] = useState(false);
  const [role, setRole] = useState<UserRole>(UserRole.NONE);
  const [contractId, setContractId] = useState<string | null>(null);
  const [userId, setUserId] = useState<string | null>(null);
  const [name, setName] = useState("");
  const [custom, setCustom] = useState<CustomCode[]>([]);
  const [customerName, setCustomerName] = useState("");

  const isChangedContractId = useMemo(() => {
    return role === UserRole.SUPER_ADMIN && contractId !== null;
  }, [contractId]);

  const authenticated = useMemo(() => {
    return role !== UserRole.NONE;
  }, [role]);

  const { callAPI: callMe } = useAPICall({
    apiMethod: me,
    backDrop: true,
    onSuccess: (res) => {
      setContractId(res.data.contract_id);
      setUserId(res.data.id);
      setRole(res.data.role);
      setName(res.data.name);
      setCustom(res.data.custom ?? []);
      setCustomerName(res.data.contract_name ?? "");
      setInitialized(true);
    },
    onFailed: () => {
      clear();
      setInitialized(true);
    },
  });

  const { callAPI: callLogin } = useAPICall({
    apiMethod: APILogin,
    backDrop: true,
    onSuccess: (res) => {
      setContractId(res.data.contract_id);
      setUserId(res.data.id);
      setRole(res.data.role);
      setName(res.data.name);
      setCustom(res.data.custom ?? []);
      setCustomerName(res.data.contract_name ?? "");
      setInitialized(true);
    },
  });

  const { callAPI: callLogout } = useAPICall({
    apiMethod: APILogout,
    onSuccess: () => {
      clear();
    },
  });
  const { callAPI: callChangeContract } = useAPICall({
    apiMethod: APIChangeContract,
    backDrop: true,
    onSuccess: (res) => {
      setContractId(res.data.contract_id);
      setCustom(res.data.custom ?? []);
      setCustomerName(res.data.contract_name ?? "");
      setInitialized(true);
    },
  });

  const clear = () => {
    setRole(UserRole.NONE);
    setContractId(null);
    setUserId(null);
    setName("");
    setCustom([]);
  };

  const login = async (email: string, password: string) => {
    const res = await callLogin({ email, password });
    if (!res) return false;

    return res.result === ResultCode.SUCCESS;
  };
  const logout = () => {
    callLogout({});
    console.info("ログアウト");
  };
  const changeContractId = async (contractId: string | null) => {
    const res = await callChangeContract({ contract_id: contractId });
    return res?.result === ResultCode.SUCCESS;
  };

  const checkRole = useCallback(
    (targetRole?: UserRole): boolean => {
      if (targetRole === undefined) return true;
      return targetRole <= role;
    },
    [role]
  );

  const canAccess = useCallback(
    (pageId: PageID): boolean => {
      const authorization = AUTH[pageId];

      // デバッグ用
      // console.log(
      //   "RET",
      //   pageId,
      //   role,
      //   custom,
      //   !!authorization &&
      //     authorization.role.includes(role) &&
      //     (authorization.custom.length === 0 ||
      //       !!custom.find((c) => {
      //         return authorization.custom.includes(c);
      //       })),
      //   [
      //     !!authorization,
      //     authorization.role.includes(role),
      //     authorization.custom.length === 0,
      //     !!custom.find((c) => {
      //       return authorization.custom.includes(c);
      //     }),
      //   ]
      // );

      return (
        !!authorization &&
        // 権限条件
        authorization.role.includes(role) &&
        // カスタム条件
        (authorization.custom.length === 0 ||
          !!custom.find((c) => {
            return authorization.custom.includes(c);
          })) &&
        // 成り代わり条件
        (authorization.allowChangedContract === undefined ||
          role !== UserRole.SUPER_ADMIN ||
          isChangedContractId === authorization.allowChangedContract)
      );
    },
    [initialized, role, custom, isChangedContractId]
  );

  useEffect(() => {
    callMe({});
  }, []);

  return (
    <AuthContext.Provider
      value={{
        // Value
        initialized,
        authenticated,
        role,
        contractId,
        userId,
        name,
        custom,
        customerName,
        isChangedContractId,

        // Func
        login,
        logout,
        changeContractId,
        checkRole,
        canAccess,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export default memo(AuthContextProvider);
