import { dashboardPayload } from '@/services/mock-data'
import { apiDelete, apiGet, apiPost, isMockApiEnabled, mockResponse, type ServiceRequestOptions } from '@/services/api-client'
import type { DownloadJob } from '@/types/domain'

export interface DownloadCreateInput {
  sourceType: 'magnet' | 'torrent' | 'direct' | 'http'
  source: string
  title?: string
}

export interface DownloadListParams {
  status?: string
  limit?: number
  offset?: number
}

export interface DownloadEvent {
  id: number
  jobId: string
  status: string
  message: string
  progressPercent: number
  payload: string
  createdAt: string
}

const authHeaders = (accessToken: string): Record<string, string> => ({
  Authorization: `Bearer ${accessToken}`,
})

const normalizeStatus = (status: string): DownloadJob['status'] => {
  switch (status) {
    case 'queued':
    case 'downloading':
    case 'stalled':
    case 'completed':
    case 'failed':
      return status
    case 'preparing':
    case 'retrying':
      return 'downloading'
    case 'cancelled':
      return 'failed'
    default:
      return 'queued'
  }
}

const toEtaMinutes = (etaSeconds?: number): number => {
  if (!etaSeconds || etaSeconds <= 0) {
    return 0
  }

  return Math.ceil(etaSeconds / 60)
}

const toDownloadJob = (value: any): DownloadJob => ({
  id: String(value.id ?? ''),
  title: String(value.title ?? value.source ?? 'Download job'),
  status: normalizeStatus(String(value.status ?? 'queued')),
  progressPercent: Number(value.progressPercent ?? 0),
  downloadSpeedMbps: Number(value.downloadSpeedMbps ?? 0),
  etaMinutes: toEtaMinutes(Number(value.etaSeconds ?? 0)),
  sourceType: (['magnet', 'torrent', 'direct', 'http'].includes(value.sourceType) ? value.sourceType : 'http') as
    | 'magnet'
    | 'torrent'
    | 'direct'
    | 'http',
})

const cloneDownloadJob = (job: DownloadJob): DownloadJob => ({
  id: job.id,
  title: job.title,
  status: job.status,
  progressPercent: job.progressPercent,
  downloadSpeedMbps: job.downloadSpeedMbps,
  etaMinutes: job.etaMinutes,
  sourceType: job.sourceType,
})

let mockDownloadJobs = dashboardPayload.activeDownloads.map(cloneDownloadJob)

export const downloadService = {
  async list(
    accessToken: string,
    params: DownloadListParams = {},
    options: ServiceRequestOptions = {},
  ): Promise<DownloadJob[]> {
    if (isMockApiEnabled()) {
      return mockResponse(
        '/api/v1/downloads',
        () => {
          let items = mockDownloadJobs.map(cloneDownloadJob)

          if (params.status) {
            items = items.filter((job) => job.status === params.status)
          }

          const offset = Math.max(0, params.offset ?? 0)
          const limit = typeof params.limit === 'number' ? Math.max(1, params.limit) : items.length
          return items.slice(offset, offset + limit)
        },
        options,
      )
    }

    const query = new URLSearchParams()

    if (params.status) query.set('status', params.status)
    if (typeof params.limit === 'number') query.set('limit', String(params.limit))
    if (typeof params.offset === 'number') query.set('offset', String(params.offset))

    const suffix = query.toString().length > 0 ? `?${query.toString()}` : ''
    const response = await apiGet<any[]>(`/api/v1/downloads${suffix}`, {}, authHeaders(accessToken))
    return response.map(toDownloadJob)
  },

  async create(
    accessToken: string,
    input: DownloadCreateInput,
    options: ServiceRequestOptions = {},
  ): Promise<DownloadJob> {
    if (isMockApiEnabled()) {
      return mockResponse(
        '/api/v1/downloads:create',
        () => {
          const nextJob: DownloadJob = {
            id: `mock-download-${Date.now()}`,
            title: input.title?.trim() || input.source,
            status: 'queued',
            progressPercent: 0,
            downloadSpeedMbps: 0,
            etaMinutes: 15,
            sourceType: input.sourceType,
          }

          mockDownloadJobs = [nextJob, ...mockDownloadJobs].map(cloneDownloadJob)
          return cloneDownloadJob(nextJob)
        },
        options,
      )
    }

    const response = await apiPost<any>('/api/v1/downloads', {
      sourceType: input.sourceType,
      source: input.source,
      title: input.title ?? '',
    }, authHeaders(accessToken))
    return toDownloadJob(response)
  },

  async cancel(accessToken: string, jobID: string, options: ServiceRequestOptions = {}): Promise<DownloadJob> {
    if (isMockApiEnabled()) {
      return mockResponse(
        `/api/v1/downloads/${jobID}:cancel`,
        () => {
          const existing = mockDownloadJobs.find((job) => job.id === jobID)
          if (!existing) {
            throw new Error('download job not found')
          }

          const cancelled = { ...existing, status: 'failed' as const, downloadSpeedMbps: 0, etaMinutes: 0 }
          mockDownloadJobs = mockDownloadJobs
            .map((job) => (job.id === jobID ? cancelled : job))
            .filter((job) => job.status !== 'failed' || job.id === jobID)

          return cloneDownloadJob(cancelled)
        },
        options,
      )
    }

    const response = await apiDelete<any>(
      `/api/v1/downloads/${encodeURIComponent(jobID)}`,
      authHeaders(accessToken),
    )
    return toDownloadJob(response)
  },

  async events(
    accessToken: string,
    jobID: string,
    after?: string,
    limit = 50,
    options: ServiceRequestOptions = {},
  ): Promise<DownloadEvent[]> {
    if (isMockApiEnabled()) {
      return mockResponse(
        `/api/v1/downloads/${jobID}/events`,
        () => {
          const job = mockDownloadJobs.find((item) => item.id === jobID)
          if (!job) {
            throw new Error('download job not found')
          }

          return [
            {
              id: 1,
              jobId: jobID,
              status: job.status,
              message: `Mock event for ${job.title}`,
              progressPercent: job.progressPercent,
              payload: '{}',
              createdAt: new Date().toISOString(),
            },
          ].slice(0, Math.max(1, limit))
        },
        options,
      )
    }

    const response = await apiGet<any[]>(
      `/api/v1/downloads/${encodeURIComponent(jobID)}/events`,
      {
        limit,
        after,
      },
      authHeaders(accessToken),
    )
    return response.map((item: any) => ({
      id: Number(item.id ?? 0),
      jobId: String(item.jobId ?? ''),
      status: String(item.status ?? ''),
      message: String(item.message ?? ''),
      progressPercent: Number(item.progressPercent ?? 0),
      payload: String(item.payload ?? '{}'),
      createdAt: String(item.createdAt ?? ''),
    }))
  },
}
