import axios from 'axios';
import * as XLSX from 'xlsx';

import { BaseApiService } from '@/services/api/base';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import {
  CreateDailyChecklistPayload,
  DailyChecklist,
  DailyChecklistReport,
  DetailDailyChecklist,
  UpdateDailyChecklistPayload,
} from '@/types/api/daily-checklist/daily-checklist';
import { isResponseError } from '@/lib/api-helper';
import { toast } from 'sonner';

export class DailyChecklistApiService extends BaseApiService<
  DailyChecklist,
  CreateDailyChecklistPayload,
  UpdateDailyChecklistPayload
> {
  constructor(basePath: string = '/daily-checklists') {
    super(basePath);
  }

  async update(id: number, payload: UpdateDailyChecklistPayload) {
    const isFormData =
      typeof FormData !== 'undefined' && payload instanceof FormData;
    try {
      const updatePath = `${this.basePath}/${id}`;

      const headers = isFormData
        ? { ...(this.header ?? {}) }
        : { 'Content-Type': 'application/json', ...(this.header ?? {}) };

      const updateRes = await httpClient<BaseApiResponse<DailyChecklist>>(
        updatePath,
        {
          method: 'PUT',
          body: payload,
          headers,
        }
      );
      return updateRes;
    } catch (error: unknown) {
      if (axios.isAxiosError<BaseApiResponse<DailyChecklist>>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async getOneDailyChecklist(id: string) {
    try {
      const getOneDailyChecklistPath = `${this.basePath}/relation/${id}`;
      const getOneDailyChecklistRes = await httpClient<
        BaseApiResponse<DetailDailyChecklist>
      >(getOneDailyChecklistPath);

      return getOneDailyChecklistRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse<DetailDailyChecklist>>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async setDailyChecklistPhase(id: string, phaseIds: string[]) {
    try {
      const setDailyChecklistPhasePath = `${this.basePath}/phase/${id}`;
      const setDailyChecklistPhaseRes = await httpClient<BaseApiResponse>(
        setDailyChecklistPhasePath,
        {
          method: 'POST',
          body: { phase_ids: phaseIds.join(',') },
        }
      );

      return setDailyChecklistPhaseRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async removeEmployeeAssignment(id: string, employeeId: string) {
    try {
      const removeEmployeeAssignmentPath = `${this.basePath}/${id}/assignments/${employeeId}`;
      const removeEmployeeAssignmentRes = await httpClient<BaseApiResponse>(
        removeEmployeeAssignmentPath,
        {
          method: 'DELETE',
        }
      );

      return removeEmployeeAssignmentRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async setDailyChecklistEmployees(checklistId: string, employeeIds: string[]) {
    try {
      const setDailyChecklistPhasePath = `${this.basePath}/assignment/${checklistId}`;
      const setDailyChecklistPhaseRes = await httpClient<BaseApiResponse>(
        setDailyChecklistPhasePath,
        {
          method: 'POST',
          body: { employee_ids: employeeIds.join(',') },
        }
      );

      return setDailyChecklistPhaseRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async getTasksByChecklistId(id: string) {
    try {
      const getTasksByChecklistIdPath = `${this.basePath}/tasks?checklist_id=${id}&page=1&limit=100`;
      const getTasksByChecklistIdRes = await httpClient<BaseApiResponse>(
        getTasksByChecklistIdPath
      );

      return getTasksByChecklistIdRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async checkOrUncheckAssignment(payload: {
    task_id: number;
    employee_id: number;
    checked: boolean;
    note: string | null;
  }) {
    try {
      const checkOrUncheckAssignmentPath = `${this.basePath}/assignment`;
      const checkOrUncheckAssignmentRes = await httpClient<BaseApiResponse>(
        checkOrUncheckAssignmentPath,
        {
          method: 'POST',
          body: payload,
        }
      );

      return checkOrUncheckAssignmentRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async submit(
    id: string,
    files: File[] = [],
    deletedDocumentIds: number[] = []
  ) {
    try {
      const formData = new FormData();

      formData.append('status', 'SUBMITTED');

      formData.append('reject_reason', '');

      files.forEach((file) => formData.append(`documents`, file));

      formData.append('deleted_document_ids', deletedDocumentIds.join(','));

      const submitPath = `${this.basePath}/${id}`;
      const submitRes = await httpClient<BaseApiResponse>(submitPath, {
        method: 'PATCH',
        body: formData,
      });

      return submitRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async approve(id: string) {
    try {
      const formData = new FormData();

      formData.append('status', 'APPROVED');

      formData.append('reject_reason', '');

      const approvePath = `${this.basePath}/${id}`;
      const approveRes = await httpClient<BaseApiResponse>(approvePath, {
        method: 'PATCH',
        body: formData,
      });

      return approveRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async bulkApprove(ids: string[]) {
    try {
      const formData = new FormData();

      formData.append('ids', ids.join(','));
      formData.append('status', 'APPROVED');
      formData.append('reject_reason', '');

      const approvePath = `${this.basePath}/bulk-update`;
      const approveRes = await httpClient<BaseApiResponse>(approvePath, {
        method: 'PATCH',
        body: formData,
      });

      return approveRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async reject(id: string, rejectReason: string) {
    try {
      const formData = new FormData();

      formData.append('status', 'REJECTED');

      formData.append('reject_reason', rejectReason);

      const rejectPath = `${this.basePath}/${id}`;
      const rejectRes = await httpClient<BaseApiResponse>(rejectPath, {
        method: 'PATCH',
        body: formData,
      });

      return rejectRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async bulkReject(ids: string[], rejectReason: string) {
    try {
      const formData = new FormData();

      formData.append('ids', ids.join(','));
      formData.append('status', 'REJECTED');
      formData.append('reject_reason', rejectReason);

      const rejectPath = `${this.basePath}/bulk-update`;
      const rejectRes = await httpClient<BaseApiResponse>(rejectPath, {
        method: 'PATCH',
        body: formData,
      });

      return rejectRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async uploadImage(
    id: number,
    status: string,
    files: File[],
    deletedDocumentIds: number[] = []
  ) {
    try {
      const formData = new FormData();

      formData.append('status', status);

      files.forEach((file) => formData.append(`documents`, file));

      formData.append('deleted_document_ids', deletedDocumentIds.join(','));

      const uploadImagePath = `${this.basePath}/${id}`;
      const uploadImageRes = await httpClient<BaseApiResponse>(
        uploadImagePath,
        {
          method: 'PATCH',
          body: formData,
        }
      );

      return uploadImageRes;
    } catch (error) {
      if (axios.isAxiosError<BaseApiResponse>(error)) {
        return error.response?.data;
      }
      return undefined;
    }
  }

  async exportDailyChecklistReportToExcel(initialQueryString: string) {
    const params = new URLSearchParams(initialQueryString);

    params.set('limit', '2000');

    const queryString = `?${params.toString()}`;

    try {
      const dailyMarketingsReport = await httpClientFetcher<
        BaseApiResponse<DailyChecklistReport[]>
      >(`${this.basePath}/report${queryString}`);

      if (isResponseError(dailyMarketingsReport)) {
        toast.error('Gagal melakukan export daily checklist! Coba lagi.');
        return;
      }

      const currentMonthMaxDay = new Date(
        Number(params.get('tahun')),
        Number(params.get('bulan')),
        0
      ).getDate();

      const rows = dailyMarketingsReport.data;

      const formattedRows = [];

      for (let i = 0; i < rows.length; i++) {
        const formattedData: Record<string, string | number> = {
          Area: rows[i].area.name,
          Farm: rows[i].farm.name,
          Kandang: rows[i].kandang.name,
          ABK: rows[i].abk.name,
          Phase: rows[i].phase,
        };

        // Add day
        for (let j = 1; j <= currentMonthMaxDay; j++) {
          formattedData[`Day ${j}`] = rows[i].daily_activities[`${j}`];
        }

        // add summary
        formattedData['Total Checklist'] = rows[i].summary.total_checklist;
        formattedData['Jumlah Hari Efektif'] =
          rows[i].summary.jumlah_hari_efektif;
        formattedData['ABK %'] = rows[i].summary.abk_percentage;
        formattedData['Kandang %'] = rows[i].summary.kandang_percentage;
        formattedData['Kategori Kurang'] = rows[i].summary.kategori.kurang;
        formattedData['Kategori Cukup'] = rows[i].summary.kategori.cukup;
        formattedData['Kategori Baik'] = rows[i].summary.kategori.baik;

        formattedRows.push(formattedData);
      }

      const ws = XLSX.utils.json_to_sheet(formattedRows);
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(
        wb,
        ws,
        `Daily Checklist ${params.get('tahun')}-${params.get('bulan')?.slice(0, 3)}`
      );

      // triggers download in browser
      XLSX.writeFile(
        wb,
        `laporan-daily-checklist-${params.get('tahun')}-${params.get('bulan')}.xlsx`
      );
    } catch {
      toast.error('Gagal melakukan export daily checklist! Coba lagi.');
    }
  }
}

export const DailyChecklistApi = new DailyChecklistApiService(
  '/daily-checklists'
);
