import axios from 'axios';
import { sleep } from '@/lib/helper';
import { BaseApiService } from '@/services/api/base';
import { BaseApiResponse, GroupedApprovals } from '@/types/api/api-general';
import {
  CreateExpensePayload,
  CreateExpenseRealizationPayload,
  Expense,
  UpdateExpensePayload,
} from '@/types/api/expense';
import { httpClient } from '@/services/http/client';

export class ExpenseApiService extends BaseApiService<
  Expense,
  FormData,
  FormData
> {
  constructor(basePath: string) {
    super(basePath);
  }

  async create(
    payload: FormData
  ): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      const createExpenseRequestRes = await httpClient<
        BaseApiResponse<Expense>
      >(this.basePath, {
        method: 'POST',
        body: payload,
      });

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

      return undefined;
    }
  }

  async createRealization(
    id: number,
    payload: FormData
  ): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      const createExpenseRealizationRes = await httpClient<
        BaseApiResponse<Expense>
      >(`${this.basePath}/${id}/realizations`, {
        method: 'POST',
        body: payload,
      });

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

      return undefined;
    }
  }

  async update(
    id: number,
    payload: FormData,
    deletedDocumentIds?: number[]
  ): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      for (const deletedDocumentId of deletedDocumentIds ?? []) {
        await this.deleteExpenseRequestDocument(id, deletedDocumentId);
      }

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

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

      return undefined;
    }
  }

  async updateRealization(
    id: number,
    payload: FormData
  ): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      const updateExpenseRealizationRes = await httpClient<
        BaseApiResponse<Expense>
      >(`${this.basePath}/${id}/realizations`, {
        method: 'PATCH',
        body: payload,
      });

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

      return undefined;
    }
  }

  async uploadRequestDocuments(
    id: number,
    files: File[]
  ): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      const updateExpenseRequestDocumentsFormData = new FormData();

      // files (multiple "documents" keys)
      files.forEach((file) => {
        updateExpenseRequestDocumentsFormData.append('documents', file);
      });

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

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

      return undefined;
    }
  }

  async uploadRealizationDocuments(
    id: number,
    files: File[]
  ): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      const updateExpenseRealizationDocumentsFormData = new FormData();

      // files (multiple "documents" keys)
      files.forEach((file) => {
        updateExpenseRealizationDocumentsFormData.append('documents', file);
      });

      const updateExpenseRealizationRes = await httpClient<
        BaseApiResponse<Expense>
      >(`${this.basePath}/${id}/realizations`, {
        method: 'PATCH',
        body: updateExpenseRealizationDocumentsFormData,
      });

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

  async complete(id: number): Promise<BaseApiResponse<Expense> | undefined> {
    try {
      const completeRes = await httpClient<BaseApiResponse<Expense>>(
        `${this.basePath}/${id}/complete`,
        {
          method: 'POST',
        }
      );

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

      return undefined;
    }
  }

  async deleteExpenseRequestDocument(
    expenseId: number,
    documentId: number
  ): Promise<BaseApiResponse | undefined> {
    try {
      const deleteExpenseRequestDocument = await httpClient<BaseApiResponse>(
        `${this.basePath}/${expenseId}/documents/${documentId}`,
        {
          method: 'DELETE',
        }
      );

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

      return undefined;
    }
  }

  async deleteExpenseRealizationDocument(
    expenseId: number,
    documentId: number
  ): Promise<BaseApiResponse | undefined> {
    try {
      const deleteExpenseRealizationDocument =
        await httpClient<BaseApiResponse>(
          `${this.basePath}/${expenseId}/realization-documents/${documentId}`,
          {
            method: 'DELETE',
          }
        );

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

      return undefined;
    }
  }

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

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

      return undefined;
    }
  }

  convertExpenseRequestPayloadToFormData = (payload: CreateExpensePayload) => {
    const formData = new FormData();

    formData.append('category', payload.category);
    formData.append('location_id', String(payload.location_id));
    formData.append('transaction_date', payload.transaction_date);
    formData.append('supplier_id', String(payload.supplier_id));

    // files (multiple "documents" keys)
    payload.documents.forEach((file) => {
      formData.append('documents', file);
    });

    formData.append(
      'expense_nonstocks',
      JSON.stringify(payload.expense_nonstocks)
    );

    return formData;
  };

  convertExpenseRequestUpdatePayloadToFormData = (
    payload: UpdateExpensePayload
  ) => {
    const formData = new FormData();

    formData.append('category', payload.category);
    formData.append('location_id', String(payload.location_id));
    formData.append('transaction_date', payload.transaction_date);
    formData.append('supplier_id', String(payload.supplier_id));

    // files (multiple "documents" keys)
    payload.documents.forEach((file) => {
      formData.append('documents', file);
    });

    formData.append(
      'expense_nonstocks',
      JSON.stringify(payload.expense_nonstocks)
    );

    return formData;
  };

  convertExpenseRealizationPayloadToFormData = (
    payload: CreateExpenseRealizationPayload
  ) => {
    const formData = new FormData();

    formData.append('realization_date', payload.realization_date);

    // files (multiple "documents" keys)
    payload.documents.forEach((file) => {
      formData.append('documents', file);
    });

    formData.append('realizations', JSON.stringify(payload.realizations));

    return formData;
  };
}

export const ExpenseApi = new ExpenseApiService('/expenses');
