import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { z } from 'zod'
import { api } from '../client'
import type { Pipeline, PipelineRun, RunDetail, StepLogs } from '../../types/api'

// ── Zod schemas ───────────────────────────────────────────────────────────────

const runStatusSchema = z.enum(['queued', 'running', 'succeeded', 'failed', 'cancelled'])

const pipelineSchema = z.object({
  id: z.number(),
  repoId: z.number(),
  name: z.string(),
  filePath: z.string(),
  createdAt: z.string(),
  updatedAt: z.string(),
})

const runSchema = z.object({
  id: z.number(),
  pipelineId: z.number(),
  repoId: z.number(),
  triggerRef: z.string(),
  triggerSha: z.string(),
  triggeredBy: z.string(),
  status: runStatusSchema,
  startedAt: z.string().nullable(),
  finishedAt: z.string().nullable(),
  createdAt: z.string(),
})

const stepSchema = z.object({
  id: z.number(),
  jobId: z.number(),
  seq: z.number(),
  name: z.string(),
  runCmd: z.string(),
  usesAction: z.string(),
  status: runStatusSchema,
  exitCode: z.number(),
  startedAt: z.string().nullable(),
  finishedAt: z.string().nullable(),
})

const jobSchema = z.object({
  id: z.number(),
  runId: z.number(),
  name: z.string(),
  image: z.string(),
  needs: z.string(),
  status: runStatusSchema,
  startedAt: z.string().nullable(),
  finishedAt: z.string().nullable(),
  createdAt: z.string(),
  steps: z.array(stepSchema),
})

const runDetailSchema = runSchema.extend({
  jobs: z.array(jobSchema),
})

const stepLogSchema = z.object({
  id: z.number(),
  stepId: z.number(),
  chunkIndex: z.number(),
  content: z.string(),
  createdAt: z.string(),
})

const stepLogsSchema = z.array(
  stepSchema.extend({ logs: z.array(stepLogSchema) }),
)

// ── Queries ───────────────────────────────────────────────────────────────────

/** Pipeline definitions for a repo. */
export function usePipelines(owner: string, repo: string) {
  return useQuery({
    queryKey: ['repos', owner, repo, 'pipelines'],
    queryFn: () =>
      api.get<Pipeline[]>(`/api/v1/repos/${owner}/${repo}/pipelines`, z.array(pipelineSchema)),
    enabled: Boolean(owner && repo),
  })
}

/** Pipeline runs for a repo, newest first. */
export function useRuns(owner: string, repo: string, limit = 30) {
  return useQuery({
    queryKey: ['repos', owner, repo, 'runs', limit],
    queryFn: () =>
      api.get<PipelineRun[]>(
        `/api/v1/repos/${owner}/${repo}/runs?limit=${limit}`,
        z.array(runSchema),
      ),
    enabled: Boolean(owner && repo),
    refetchInterval: 8_000, // poll while runs may be active
  })
}

/** Run detail: run + jobs (each with steps). */
export function useRunDetail(owner: string, repo: string, runId: number) {
  return useQuery({
    queryKey: ['repos', owner, repo, 'runs', runId],
    queryFn: () =>
      api.get<RunDetail>(
        `/api/v1/repos/${owner}/${repo}/runs/${runId}`,
        runDetailSchema,
      ),
    enabled: Boolean(owner && repo && runId),
    refetchInterval: (query) => {
      const status = query.state.data?.status
      return status === 'running' || status === 'queued' ? 3_000 : false
    },
  })
}

/** Step-level log chunks for a job. */
export function useJobLogs(owner: string, repo: string, runId: number, jobId: number) {
  return useQuery({
    queryKey: ['repos', owner, repo, 'runs', runId, 'jobs', jobId, 'logs'],
    queryFn: () =>
      api.get<StepLogs>(
        `/api/v1/repos/${owner}/${repo}/runs/${runId}/jobs/${jobId}/logs`,
        stepLogsSchema,
      ),
    enabled: Boolean(owner && repo && runId && jobId),
    refetchInterval: (query) => {
      // Keep polling only while the job may still be running
      const hasRunning = query.state.data?.some(s => s.status === 'running')
      return hasRunning ? 2_000 : false
    },
  })
}

/** Recent pipeline runs across all repos owned by the current user. */
export function useRecentRuns(limit = 20) {
  return useQuery({
    queryKey: ['pipelines', 'runs', limit],
    queryFn: () =>
      api.get<RecentRun[]>(`/api/v1/pipelines/runs?limit=${limit}`, recentRunSchema),
    refetchInterval: 10_000,
  })
}

// ── Mutations ─────────────────────────────────────────────────────────────────

export function useCancelRun(owner: string, repo: string) {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (runId: number) =>
      api.post(`/api/v1/repos/${owner}/${repo}/runs/${runId}/cancel`, z.unknown(), undefined),
    onSuccess: (_data, runId) => {
      qc.invalidateQueries({ queryKey: ['repos', owner, repo, 'runs'] })
      qc.invalidateQueries({ queryKey: ['repos', owner, repo, 'runs', runId] })
      qc.invalidateQueries({ queryKey: ['pipelines', 'runs'] })
    },
  })
}

export function useRetryJob(owner: string, repo: string, runId: number) {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (jobId: number) =>
      api.post(
        `/api/v1/repos/${owner}/${repo}/runs/${runId}/jobs/${jobId}/retry`,
        z.unknown(),
        undefined,
      ),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['repos', owner, repo, 'runs', runId] })
      qc.invalidateQueries({ queryKey: ['repos', owner, repo, 'runs'] })
      qc.invalidateQueries({ queryKey: ['pipelines', 'runs'] })
    },
  })
}

// ── Cross-repo recent run type ─────────────────────────────────────────────────
// Returned by GET /api/v1/pipelines/runs — extends PipelineRun with repo context.

export interface RecentRun extends PipelineRun {
  repoName: string
  ownerName: string
}

const recentRunSchema = z.array(
  runSchema.extend({
    repoName: z.string(),
    ownerName: z.string(),
  }),
)
