import { ref, watch } from 'vue';
import { defineStore } from 'pinia';

import { api } from 'src/boot/axios';
import useFlowStore from '../flow';
import { Pagination } from '../types';
import { BankBook, Branch, BranchCreate } from './types';
import { BranchContact } from '../branch-contact/types';
import { User } from '../user/types';

type BranchId = string;

const useBranchStore = defineStore('api-branch', () => {
  const flowStore = useFlowStore();
  const data = ref<Pagination<(Branch & { branch?: Branch[] })[]>>({
    result: [],
    page: 0,
    pageSize: 0,
    total: 0,
  });
  const map = ref<Record<BranchId, Branch & { contact?: BranchContact[] }>>({});
  const contact: Record<BranchId, BranchContact[]> = {};

  watch(data, () => {
    data.value.result.forEach((v) => {
      map.value[v.id] = v;
    });
  });

  async function fetchList<
    Options extends {
      page?: number;
      pageSize?: number;
      zipCode?: string;
      query?: string;
      tree?: boolean;
      filter?: 'head' | 'sub';
    },
    Data extends Pagination<Branch[]>,
  >(opts?: Options): Promise<Data | false> {
    const params = new URLSearchParams();

    for (const [k, v] of Object.entries(opts || {})) {
      if (v !== undefined) params.append(k, v.toString());
    }

    const query = params.toString();

    const res = await api.get<Data>(
      `/branch${(params && '?'.concat(query)) || ''}`,
      { headers: { 'X-Rtid': flowStore.rtid } },
    );

    if (res && res.status === 200) {
      data.value = res.data;
      return res.data;
    }
    return false;
  }

  async function fetchById<
    Options extends { includeSubBranch?: boolean; includeContact?: boolean },
    Data extends Branch &
      (Options['includeSubBranch'] extends true
        ? { branch: Branch[] }
        : unknown) &
      (Options['includeContact'] extends true
        ? { contact: BranchContact[] }
        : unknown),
  >(id: string, opts?: Options): Promise<Data | false> {
    const params = new URLSearchParams();

    for (const [k, v] of Object.entries(opts || {})) {
      if (v !== undefined) params.append(k, v.toString());
    }

    const query = params.toString();

    const res = await api.get<Data>(
      `/branch/${id}${(params && '?'.concat(query)) || ''}`,
      { headers: { 'X-Rtid': flowStore.rtid } },
    );

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function fetchImageListById(
    id: string,
    flow?: {
      sessionId?: string;
      refTransactionId?: string;
      transactionId?: string;
    },
  ) {
    const res = await api.get(`/branch/${id}/image`, {
      headers: {
        'X-Session-Id': flow?.sessionId,
        'X-Rtid': flow?.refTransactionId || flowStore.rtid,
        'X-Tid': flow?.transactionId,
      },
    });

    if (!res) return false;
    if (res.status === 200) return res.data;
    if (res.status === 204) return null;

    return false;
  }

  async function addImageList(file: File, branchId: string, name: string) {
    await api
      .put(`/branch/${branchId}/image/${name}`, file, {
        headers: { 'Content-Type': file.type },
        onUploadProgress: (e) => console.log(e),
      })
      .catch((e) => console.error(e));

    return name;
  }

  async function deleteImageByName(
    id: string,
    name: string,
    flow?: {
      sessionId?: string;
      refTransactionId?: string;
      transactionId?: string;
    },
  ) {
    const res = await api.delete(`/branch/${id}/image/${name}`, {
      headers: {
        'X-Session-Id': flow?.sessionId,
        'X-Rtid': flow?.refTransactionId || flowStore.rtid,
        'X-Tid': flow?.transactionId,
      },
    });

    if (!res) return false;
  }

  async function create(
    branch: BranchCreate,
    bank?: BankBook[],
    imgList?: {
      selectedImage: string;
      list: { url: string; imgFile: File | null; name: string }[];
    },
  ) {
    const { qrCodeImage, zipCode, ...payload } = branch;
    const bankPayload = bank?.map(({ bankUrl, bankQr, ...rest }) => ({
      ...rest,
    }));

    const res = await api.post<Branch>(
      '/branch',
      {
        ...payload,
        selectedImage: (imgList && imgList.selectedImage) || '',
        bank: bankPayload,
      },
      { headers: { 'X-Rtid': flowStore.rtid } },
    );

    if (qrCodeImage) {
      await api
        .put(`/branch/${res.data.id}/line-image`, qrCodeImage, {
          headers: { 'Content-Type': qrCodeImage.type },
          onUploadProgress: (e) => console.log(e),
        })
        .catch((e) => console.error(e));
    }

    if (res.data.bank && bank) {
      for (let i = 0; i < bank?.length; i++) {
        if (bank[i].bankQr) {
          await api
            .put(
              `/branch/${res.data.id}/bank-qr/${res.data.bank[i].id}`,
              bank[i].bankQr,
              {
                headers: { 'Content-Type': bank[i].bankQr?.type },
                onUploadProgress: (e) => console.log(e),
              },
            )
            .catch((e) => console.error(e));
        }
      }
    }

    if (imgList && imgList.list.length > 0 && res.data.id) {
      for (let index = 0; index < imgList.list.length; index++) {
        const imgFile = imgList.list[index].imgFile;
        if (imgFile)
          await addImageList(imgFile, res.data.id, imgList.list[index].name);
      }
    }

    if (!res) return false;

    return res.data;
  }

  async function editById(
    id: string,
    data: Partial<BranchCreate & { status: 'ACTIVE' | 'INACTIVE' | 'CREATED' }>,
    qrCodeImage?: File | undefined,
    bank?: BankBook[],
    opts?: {
      deleteQrCodeImage?: boolean;
      indexDeleteQrCodeBank?: number[];
    },
  ) {
    const { zipCode, ...payload } = data;

    const bankPayload = bank?.map(({ branchId, bankQr, bankUrl, ...rest }) => ({
      ...rest,
    }));

    const res = await api.put<Branch>(
      `/branch/${id}`,
      { ...payload, bank: bankPayload },
      {
        headers: { 'X-Rtid': flowStore.rtid },
      },
    );

    if (qrCodeImage !== undefined && qrCodeImage !== null) {
      await api
        .put(`/branch/${res.data.id}/line-image`, qrCodeImage, {
          headers: { 'Content-Type': qrCodeImage.type },
          onUploadProgress: (e) => console.log(e),
        })
        .catch((e) => console.error(e));
    }
    if (opts?.deleteQrCodeImage === true) {
      await api
        .delete(`/branch/${res.data.id}/line-image`)
        .catch((e) => console.error(e));
    }

    if (!!res.data.bank && !!bank) {
      for (let i = 0; i < bank?.length; i++) {
        if (bank?.[i].bankQr) {
          await api
            .put(
              `/branch/${res.data.id}/bank-qr/${res.data.bank[i].id}`,
              bank[i].bankQr,
              {
                headers: { 'Content-Type': bank[i].bankQr?.type },
                onUploadProgress: (e) => console.log(e),
              },
            )
            .catch((e) => console.error(e));
        }
      }

      if (
        opts?.indexDeleteQrCodeBank !== undefined &&
        opts?.indexDeleteQrCodeBank?.length >= 0
      ) {
        opts.indexDeleteQrCodeBank.forEach(async (i) => {
          await api
            .delete(`/branch/${res.data.id}/bank-qr/${res.data.bank[i].id}`)
            .catch((e) => console.error(e));
        });
      }
    }

    if (!res) return false;

    return res.data;
  }

  async function deleteById(id: string) {
    const res = await api.delete<Branch>(`/branch/${id}`, {
      headers: {
        'X-Rtid': flowStore.rtid,
      },
    });

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function getUser(branchId: string) {
    const res = await api.get(`/branch/${branchId}/user`, {
      headers: { 'X-Rtid': flowStore.rtid },
    });

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function getAdmin(branchId: string | string[]) {
    const res = await api.get<User>(`/branch/${branchId}/manager`, {
      headers: { 'X-Rtid': flowStore.rtid },
    });

    if (!res) return false;
    if (res.status === 200) return res.data;
    if (res.status === 204) return null;

    return false;
  }

  async function addUser(branchId: string, userId: string | string[]) {
    const res = await api.post(
      `/branch/${branchId}/user`,
      { user: ([] as string[]).concat(userId) },
      { headers: { 'X-Rtid': flowStore.rtid } },
    );

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function removeUser(branchId: string, userId: string | string[]) {
    const res = await api.delete(`/branch/${branchId}/user`, {
      headers: { 'X-Rtid': flowStore.rtid },
      data: { user: ([] as string[]).concat(userId) },
    });

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function stats(opts?: { headOfficeId?: string }) {
    const params = new URLSearchParams();

    for (const [k, v] of Object.entries(opts || {})) {
      v !== undefined && params.append(k, v.toString());
    }

    const query = params.toString();

    const res = await api.get<{
      hq: number;
      br: number;
      virtual: number;
    }>(`/branch/stats${(params && '?'.concat(query)) || ''}`, {
      headers: {
        'X-Rtid': flowStore.rtid,
      },
    });

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function userStats(userType: string) {
    const res = await api.get(
      `/branch/user-stats${userType ? '?'.concat(userType) : ''}`,
      {
        headers: { 'X-Rtid': flowStore.rtid },
      },
    );

    if (!res) return false;
    if (res.status === 200) return res.data;

    return false;
  }

  async function getContact(branchId: BranchId, force = false) {
    if (!force && contact[branchId]) return contact[branchId];

    const res = await fetchById(branchId, { includeContact: true });

    if (res) {
      contact[branchId] = res.contact;
      return res.contact;
    }

    return false;
  }

  async function fetchListAttachment(branchId: string) {
    const res = await api.get<string[]>(`branch/${branchId}/attachment`);

    if (res.status === 200) {
      return res.data;
    }
  }

  async function fetchByIdAttachment(branchId: string, name: string) {
    const res = await api.get<string>(`branch/${branchId}/attachment/${name}`);

    if (res.status === 200) {
      return res.data;
    }
  }

  async function putAttachment(branchId: string, fileList: File[]) {
    fileList.forEach(async (file) => {
      await api
        .put(`branch/${branchId}/attachment/${file.name}`, file, {
          headers: { 'Content-Type': file.type },
          onUploadProgress: (e) => console.log(e),
        })
        .catch((e) => console.error(e));
    });
  }

  async function deleteByIdAttachment(branchId: string, name: string) {
    const res = await api.delete(`branch/${branchId}/attachment/${name}`);

    if (res.status === 204) {
      return true;
    }

    return false;
  }

  return {
    data,
    map,
    fetchList,
    fetchById,

    fetchImageListById,
    addImageList,
    deleteImageByName,

    create,
    editById,
    deleteById,

    getAdmin,
    getUser,
    addUser,
    removeUser,

    getContact,

    stats,
    userStats,

    fetchListAttachment,
    fetchByIdAttachment,
    putAttachment,
    deleteByIdAttachment,
  };
});

export default useBranchStore;
