import {
  reservationTaskAudits,
  reservationTaskDetails,
} from '@/fixtures/reservation'
import { getReservationHotelId } from '@/config/reservationConfig'
import { getJson, sendJson } from '@/services/httpClient'
import type {
  ReservationManualReviewConversionRequest,
  ReservationManualReviewConversionResult,
  ReservationOperaOperationResult,
  ReservationOperaSimulationRequest,
  ReservationOrderDetailResult,
  ReservationOrderListFilters,
  ReservationOrderListResult,
  ReservationTaskAuditListResult,
  ReservationTaskDetailResult,
  ReservationTaskListFilters,
  ReservationTaskListResult,
  ReservationTaskPayloadMutationRequest,
  ReservationTaskPayloadMutationResult,
  SourceMessageConversationResult,
} from '@/types/reservation'

const useReservationFixtures = import.meta.env.VITE_RESERVATION_USE_FIXTURES === 'true'

export async function fetchReservationOrders(
  filters: ReservationOrderListFilters = {},
): Promise<ReservationOrderListResult> {
  return getJson<ReservationOrderListResult>(withQuery('/api/reservation/orders', withReservationHotel(filters)))
}

export async function fetchReservationTaskList(
  filters: ReservationTaskListFilters = {},
): Promise<ReservationTaskListResult> {
  return getJson<ReservationTaskListResult>(withQuery('/api/reservation/tasks', withReservationHotel(filters)))
}

export async function fetchReservationOrderDetail(orderId: string): Promise<ReservationOrderDetailResult> {
  return getJson<ReservationOrderDetailResult>(
    withQuery(`/api/reservation/orders/${encodeURIComponent(orderId)}`, {
      hotel_id: getReservationHotelId(),
      include_tasks: true,
      include_source_summary: true,
    }),
  )
}

export async function fetchSourceMessageConversation(
  sourceMessageId: string,
): Promise<SourceMessageConversationResult> {
  return getJson<SourceMessageConversationResult>(
    `/api/source-messages/${encodeURIComponent(sourceMessageId)}/conversation`,
  )
}

export async function fetchReservationTaskDetail(taskId: string): Promise<ReservationTaskDetailResult> {
  if (useReservationFixtures) {
    return fixtureTaskDetail(taskId)
  }
  return getJson<ReservationTaskDetailResult>(`/api/reservation/tasks/${taskId}`)
}

export async function saveReservationTaskDraft(
  taskId: string,
  request: ReservationTaskPayloadMutationRequest,
): Promise<ReservationTaskPayloadMutationResult> {
  if (useReservationFixtures) {
    return fixtureTaskMutation(taskId, request, false)
  }
  return sendJson<ReservationTaskPayloadMutationResult>(`/api/reservation/tasks/${taskId}/draft`, 'PUT', request)
}

export async function confirmReservationTask(
  taskId: string,
  request: ReservationTaskPayloadMutationRequest,
): Promise<ReservationTaskPayloadMutationResult> {
  if (useReservationFixtures) {
    return fixtureTaskMutation(taskId, request, true)
  }
  return sendJson<ReservationTaskPayloadMutationResult>(`/api/reservation/tasks/${taskId}/confirm`, 'POST', request)
}

export async function convertReservationManualReviewTask(
  taskId: string,
  request: ReservationManualReviewConversionRequest,
): Promise<ReservationManualReviewConversionResult> {
  return sendJson<ReservationManualReviewConversionResult>(
    `/api/reservation/tasks/${encodeURIComponent(taskId)}/manual-review-conversions`,
    'POST',
    request,
  )
}

export async function executeReservationOperaOperation(
  taskId: string,
  operationId: string,
  request: ReservationOperaSimulationRequest,
): Promise<ReservationOperaOperationResult> {
  if (useReservationFixtures) {
    return fixtureOperaOperation(taskId, operationId, request, 'execute')
  }
  return sendJson<ReservationOperaOperationResult>(
    `/api/reservation/tasks/${taskId}/opera-operations/${operationId}/execute`,
    'POST',
    request,
  )
}

export async function retryReservationOperaOperation(
  taskId: string,
  operationId: string,
  request: ReservationOperaSimulationRequest,
): Promise<ReservationOperaOperationResult> {
  if (useReservationFixtures) {
    return fixtureOperaOperation(taskId, operationId, request, 'retry')
  }
  return sendJson<ReservationOperaOperationResult>(
    `/api/reservation/tasks/${taskId}/opera-operations/${operationId}/retry`,
    'POST',
    request,
  )
}

export async function fetchReservationTaskAudits(taskId: string): Promise<ReservationTaskAuditListResult> {
  if (useReservationFixtures) {
    return Promise.resolve(
      reservationTaskAudits.find((audit) => audit.task_id === taskId) ?? {
        task_id: taskId,
        items: [],
      },
    )
  }
  return getJson<ReservationTaskAuditListResult>(`/api/reservation/tasks/${taskId}/audits`)
}

function withQuery(path: string, params: object): string {
  const searchParams = new URLSearchParams()
  Object.entries(params).forEach(([key, value]) => {
    if (
      value === undefined ||
      value === null ||
      value === '' ||
      !['string', 'number', 'boolean'].includes(typeof value)
    ) {
      return
    }
    searchParams.set(key, String(value))
  })
  const query = searchParams.toString()
  return query ? `${path}?${query}` : path
}

function withReservationHotel<T extends { hotel_id?: string }>(filters: T): T {
  return {
    hotel_id: getReservationHotelId(),
    ...filters,
  }
}

function fixtureTaskDetail(taskId: string): Promise<ReservationTaskDetailResult> {
  const detail = reservationTaskDetails.find((item) => item.task_id === taskId)
  if (!detail) {
    throw new Error(`Reservation task ${taskId} was not found in frontend fixture data.`)
  }
  return Promise.resolve(structuredClone(detail))
}

function fixtureTaskMutation(
  taskId: string,
  request: ReservationTaskPayloadMutationRequest,
  confirmed: boolean,
): Promise<ReservationTaskPayloadMutationResult> {
  return fixtureTaskDetail(taskId).then((detail) => ({
    task_id: detail.task_id,
    order_id: detail.order_id,
    task_status: confirmed ? 'READY' : detail.task_status,
    draft_payload: request.field_values,
    confirmed_payload: confirmed ? request.field_values : detail.confirmed_payload,
    opera_operations: confirmed ? detail.opera_operations : [],
  }))
}

async function fixtureOperaOperation(
  taskId: string,
  operationId: string,
  request: ReservationOperaSimulationRequest,
  action: 'execute' | 'retry',
): Promise<ReservationOperaOperationResult> {
  const detail = await fixtureTaskDetail(taskId)
  const operation = detail.opera_operations.find((item) => item.operation_id === operationId)
  if (!operation) {
    throw new Error(`Reservation OPERA operation ${operationId} was not found in frontend fixture data.`)
  }

  const success = request.simulate_success !== false
  const attemptNumber = operation.attempt_count + 1
  return {
    ...operation,
    operation_status: success ? 'SUCCEEDED' : 'FAILED',
    attempt_count: attemptNumber,
    last_error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure',
    attempts: [
      ...operation.attempts,
      {
        attempt_id: `${operationId}-${action}-${attemptNumber}`,
        attempt_number: attemptNumber,
        attempt_status: success ? 'SUCCEEDED' : 'FAILED',
        response_payload: success ? { simulated: true } : null,
        error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure',
        started_at: new Date().toISOString(),
        finished_at: new Date().toISOString(),
      },
    ],
  }
}
