import * as XLSX from 'xlsx';
import axios from 'axios';
import { BaseApiService } from '@/services/api/base';
import {
  Approvals,
  BaseApiResponse,
  GroupedApprovals,
} from '@/types/api/api-general';

import {
  CreateTransferToLayingPayload,
  TransferToLaying,
  UpdateTransferToLayingPayload,
} from '@/types/api/production/transfer-to-laying';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import {
  ProjectFlockAvailableQuantity,
  ProjectFlockMaxQuantity,
} from '@/types/api/production/project-flock';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import toast from 'react-hot-toast';
import { formatDate } from '@/lib/helper';

export class TransferToLayingService extends BaseApiService<
  TransferToLaying,
  CreateTransferToLayingPayload,
  UpdateTransferToLayingPayload
> {
  constructor(basePath: string = '') {
    super(basePath);
  }

  async approve(
    id: number,
    notes?: string
  ): Promise<BaseApiResponse<TransferToLaying> | undefined> {
    try {
      const approveRes = await httpClient<BaseApiResponse<TransferToLaying>>(
        `${this.basePath}/approvals`,
        {
          method: 'POST',
          body: {
            action: 'APPROVED',
            approvable_ids: [id],
            notes: notes,
          },
        }
      );

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

      return undefined;
    }
  }

  async bulkApprove(
    ids: number[],
    notes?: string
  ): Promise<BaseApiResponse<TransferToLaying> | undefined> {
    try {
      const approveRes = await httpClient<BaseApiResponse<TransferToLaying>>(
        `${this.basePath}/approvals`,
        {
          method: 'POST',
          body: {
            action: 'APPROVED',
            approvable_ids: ids,
            notes: notes,
          },
        }
      );

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

      return undefined;
    }
  }

  async reject(
    id: number,
    notes?: string
  ): Promise<BaseApiResponse<TransferToLaying> | undefined> {
    try {
      const rejectRes = await httpClient<BaseApiResponse<TransferToLaying>>(
        `${this.basePath}/approvals`,
        {
          method: 'POST',
          body: {
            action: 'REJECTED',
            approvable_ids: [id],
            notes: notes,
          },
        }
      );

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

      return undefined;
    }
  }

  async bulkReject(
    ids: number[],
    notes?: string
  ): Promise<BaseApiResponse<TransferToLaying> | undefined> {
    try {
      const rejectRes = await httpClient<BaseApiResponse<TransferToLaying>>(
        `${this.basePath}/approvals`,
        {
          method: 'POST',
          body: {
            action: 'REJECTED',
            approvable_ids: ids,
            notes: notes,
          },
        }
      );

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

      return undefined;
    }
  }

  async getAvailableQty(projectFlockId: number) {
    try {
      const availableQtyRes = await httpClient<
        BaseApiResponse<ProjectFlockAvailableQuantity>
      >(`${this.basePath}/project-flocks/${projectFlockId}/available-qty`);

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

      return undefined;
    }
  }

  async getMappedFlockKandangsAvailability(projectFlockId: number) {
    try {
      const flockAvailableQty = await this.getAvailableQty(projectFlockId);

      const flockKandangsAvailableQty = isResponseSuccess(flockAvailableQty)
        ? flockAvailableQty.data.kandangs
        : [];

      const mappedFlockKandangsAvailableQty: Record<
        number,
        (typeof flockKandangsAvailableQty)[0]
      > = {};

      flockKandangsAvailableQty.forEach((item) => {
        if (!mappedFlockKandangsAvailableQty[item.project_flock_kandang_id]) {
          mappedFlockKandangsAvailableQty[item.project_flock_kandang_id] = item;
        }
      });

      return mappedFlockKandangsAvailableQty;
    } catch {
      return undefined;
    }
  }

  async getMaxTargetQty(projectFlockId: number) {
    try {
      const availableQtyRes = await httpClient<
        BaseApiResponse<ProjectFlockMaxQuantity>
      >(`${this.basePath}/project-flocks/${projectFlockId}/max-target-qty`);

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

      return undefined;
    }
  }

  async getMappedFlockKandangsMaxTargetQty(projectFlockId: number) {
    try {
      const flockMaxTargetQty = await this.getMaxTargetQty(projectFlockId);

      const flockKandangsMaxTargetQty = isResponseSuccess(flockMaxTargetQty)
        ? flockMaxTargetQty.data.project_flock_kandangs
        : [];

      const mappedFlockKandangsMaxTargetQty: Record<
        number,
        (typeof flockKandangsMaxTargetQty)[0]
      > = {};

      flockKandangsMaxTargetQty.forEach((item) => {
        if (!mappedFlockKandangsMaxTargetQty[item.project_flock_kandang_id]) {
          mappedFlockKandangsMaxTargetQty[item.project_flock_kandang_id] = item;
        }
      });

      return mappedFlockKandangsMaxTargetQty;
    } catch {
      return undefined;
    }
  }

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

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

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

    try {
      const transferToLayings = await httpClientFetcher<
        BaseApiResponse<TransferToLaying[]>
      >(`${this.basePath}${queryString}`);

      if (isResponseError(transferToLayings)) {
        toast.error('Gagal melakukan export transfer to laying! Coba lagi.');
        return;
      }

      const rows = transferToLayings.data;

      const formattedRows = [];

      for (let i = 0; i < rows.length; i++) {
        formattedRows.push({
          id: rows[i].id,
          transfer_number: rows[i].transfer_number,
          transfer_date: formatDate(rows[i].transfer_date, 'DD-MM-YYYY'),
          project_flock_source: rows[i].from_project_flock.flock_name,
          project_flock_target: rows[i].to_project_flock.flock_name,
          pending_usage_qty: rows[i].pending_usage_qty,
          usage_qty: rows[i].usage_qty,
          project_flock_source_kandang: rows[i].sources
            .map((item) => item.source_project_flock_kandang.kandang.name)
            .join(', '),
          project_flock_target_kandang: rows[i].targets
            .map((item) => item.target_project_flock_kandang.kandang.name)
            .join(', '),
          created_user: rows[i].created_user.name,
          created_at: formatDate(rows[i].created_at, 'DD-MM-YYYY'),
          updated_at: formatDate(rows[i].updated_at, 'DD-MM-YYYY'),
          notes: rows[i].notes,
        });
      }

      const ws = XLSX.utils.json_to_sheet(formattedRows);
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, ws, 'transfer-ke-laying');

      // triggers download in browser
      XLSX.writeFile(wb, 'transfer-ke-laying.xlsx');
    } catch {
      toast.error('Gagal melakukan export transfer to laying! Coba lagi.');
    }
  }

  async getApprovalHistory(
    transferToLayingId: number,
    group: boolean = true,
    page: number = 1,
    limit: number = 10
  ) {
    try {
      const approvalHistoryRes = await httpClient<GroupedApprovals>(
        '/approvals',
        {
          query: {
            module_name: 'TRANSFER_TO_LAYINGS',
            module_id: transferToLayingId,
            group_step_number: group ? 'true' : 'false',
            page,
            limit,
          },
        }
      );

      return approvalHistoryRes;
    } catch (error) {
      if (axios.isAxiosError<GroupedApprovals>(error)) {
        return error.response?.data;
      }

      return undefined;
    }
  }

  async getApprovalLineHistory(
    transferToLayingId: number,
    page: number = 1,
    limit: number = 100
  ) {
    try {
      const approvalHistoryRes = await httpClient<Approvals>('/approvals', {
        query: {
          module_name: 'TRANSFER_TO_LAYINGS',
          module_id: transferToLayingId,
          group_step_number: 'false',
          page,
          limit,
          order_by_date: 'ASC',
        },
      });

      return approvalHistoryRes;
    } catch (error) {
      if (axios.isAxiosError<Approvals>(error)) {
        return error.response?.data;
      }

      return undefined;
    }
  }
}

export const TransferToLayingApi = new TransferToLayingService(
  '/production/transfer_layings'
);
