import { type Expense, EXPENSE_TYPE, type ExpenseType } from "src/modules/timesheets/models/expense.models";
import type { Normalizer } from "src/utils/normalize-object";

export interface ApiErrorPayload {
  status_code: number;
  error_code?: string;
  message?: string;
  context?: Record<string, unknown>;
};

export abstract class ApiError extends Error {
  status_code: number;
  error_code?: string;
  context?: Record<string, unknown>;

  constructor(payload: ApiErrorPayload, defaultMessage: string) {
    super(payload.message || defaultMessage);
    this.status_code = payload.status_code;
    this.error_code = payload.error_code ?? "unknown";
    this.context = payload.context ?? {'unknown': 'unknown error has occured', };
  }
};

export class GenericApiError extends ApiError {
    constructor(payload: ApiErrorPayload) {
        super(payload, 'Encountered an error processing request');
        this.name = 'GenericApiError';
    }
};

export class ExpensesValidationError extends ApiError {
  constructor(payload: ApiErrorPayload) {
    super(payload, 'Invalid expense payload');
    this.name = 'ExpensesValidationError';
  }
};

export class ExpensesApiError extends ApiError {
  constructor(payload: ApiErrorPayload) {
    super(payload, 'Request failed');
    this.name = 'ExpensesApiError';
  }
};

export const expense_validation_schema: Normalizer<Expense> = {
    date:               v => typeof v === 'string' ? v.trim() : '1970-01-01',
    type:               v => EXPENSE_TYPE.includes(v as ExpenseType) ? v as ExpenseType : "EXPENSES",
    amount:             v => typeof v === "number" ? v : -1,
    mileage:            v => typeof v === "number" ? v : undefined,
    comment:            v => typeof v === 'string' ? v.trim() : '',
    supervisor_comment: v => typeof v === 'string' ? v.trim() : '',
    is_approved:        v => !!v,
};

export function toExpensesError(err: unknown): ExpensesValidationError | ExpensesApiError {
    if (err instanceof ExpensesValidationError || err instanceof ExpensesApiError) {
        return err;
    }

    if (typeof err === 'object' && err !== null && 'status_code' in err) {
        const payload = err as ApiErrorPayload;

        // Don't know how to differentiate both types of errors, can be updated here
        if (payload.error_code?.startsWith('API_')) {
            return new ExpensesApiError(payload);
        }

        return new ExpensesValidationError(payload);
    }

    // Fallback with ValidationError as default
    return new ExpensesValidationError({
        status_code: 500,
        message: err instanceof Error ? err.message : 'Unknown error',
        context: { original: err }
    });
}
