import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useAuthStore } from '../../auth/auth-store'
import { config } from '../../config'

// ── Application Config ────────────────────────────────────────────────────

export interface TapDefinition {
  tapId: string
  processorId: string
  target: 'INPUT' | 'OUTPUT' | 'BOTH'
  expression: string
  language: string
  attributeName: string
  attributeType: 'BUSINESS_OBJECT' | 'CORRELATION' | 'EVENT' | 'CUSTOM'
  enabled: boolean
  version: number
}

export interface ApplicationConfig {
  application: string
  version: number
  updatedAt?: string
  engineLevel?: string
  payloadCaptureMode?: string
  applicationLogLevel?: string
  agentLogLevel?: string
  metricsEnabled: boolean
  samplingRate: number
  tracedProcessors: Record<string, string>
  taps: TapDefinition[]
  tapVersion: number
  routeRecording: Record<string, boolean>
  compressSuccess: boolean
  sensitiveKeys?: string[]
  globalSensitiveKeys?: string[]
  mergedSensitiveKeys?: string[]
}

/** Authenticated fetch using the JWT from auth store. Paths are relative to apiBaseUrl. */
function authFetch(path: string, init?: RequestInit): Promise<Response> {
  const token = useAuthStore.getState().accessToken
  const headers = new Headers(init?.headers)
  if (token) headers.set('Authorization', `Bearer ${token}`)
  headers.set('X-Cameleer-Protocol-Version', '1')
  return fetch(`${config.apiBaseUrl}${path}`, { ...init, headers })
}

export function useAllApplicationConfigs(environment: string | undefined) {
  return useQuery({
    queryKey: ['applicationConfig', 'all', environment],
    queryFn: async () => {
      const res = await authFetch(`/environments/${encodeURIComponent(environment!)}/config`)
      if (!res.ok) throw new Error('Failed to fetch configs')
      return res.json() as Promise<ApplicationConfig[]>
    },
    enabled: !!environment,
  })
}

export function useApplicationConfig(application: string | undefined, environment: string | undefined) {
  return useQuery({
    queryKey: ['applicationConfig', environment, application],
    queryFn: async () => {
      const res = await authFetch(
        `/environments/${encodeURIComponent(environment!)}/apps/${encodeURIComponent(application!)}/config`)
      if (!res.ok) throw new Error(`Failed to fetch config: ${res.status}`)
      const data = await res.json()
      // Server returns AppConfigResponse: { config, globalSensitiveKeys, mergedSensitiveKeys }
      const cfg = data.config ?? data
      cfg.globalSensitiveKeys = data.globalSensitiveKeys ?? null
      cfg.mergedSensitiveKeys = data.mergedSensitiveKeys ?? null
      return cfg as ApplicationConfig
    },
    enabled: !!application && !!environment,
  })
}

export interface ConfigUpdateResponse {
  config: ApplicationConfig
  pushResult: CommandGroupResponse
}

export function useUpdateApplicationConfig() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: async ({ config, environment, apply = 'live' }: {
      config: ApplicationConfig;
      environment: string;
      apply?: 'staged' | 'live';
    }) => {
      const res = await authFetch(
        `/environments/${encodeURIComponent(environment)}/apps/${encodeURIComponent(config.application)}/config?apply=${apply}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(config),
      })
      if (!res.ok) throw new Error('Failed to update config')
      return res.json() as Promise<ConfigUpdateResponse>
    },
    onSuccess: (result, vars) => {
      queryClient.setQueryData(['applicationConfig', vars.environment, result.config.application], result.config)
      queryClient.invalidateQueries({ queryKey: ['applicationConfig'] })
    },
  })
}

// ── Processor → Route Mapping ─────────────────────────────────────────────

export function useProcessorRouteMapping(application?: string, environment?: string) {
  return useQuery({
    queryKey: ['config', environment, application, 'processor-routes'],
    queryFn: async () => {
      const res = await authFetch(
        `/environments/${encodeURIComponent(environment!)}/apps/${encodeURIComponent(application!)}/processor-routes`)
      if (!res.ok) throw new Error('Failed to fetch processor-route mapping')
      return res.json() as Promise<Record<string, string>>
    },
    enabled: !!application && !!environment,
  })
}

// ── Group Command Response ───────────────────────────────────────────────

export interface CommandGroupResponse {
  success: boolean
  total: number
  responded: number
  responses: { agentId: string; status: string; message: string }[]
  timedOut: string[]
}

// ── Generic Group Command (kept for non-config commands) ──────────────────

interface SendGroupCommandParams {
  group: string
  type: string
  payload: Record<string, unknown>
  environment?: string
}

export function useSendGroupCommand() {
  return useMutation({
    mutationFn: async ({ group, type, payload, environment }: SendGroupCommandParams) => {
      const envParam = environment ? `?environment=${encodeURIComponent(environment)}` : ''
      const res = await authFetch(`/agents/groups/${encodeURIComponent(group)}/commands${envParam}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ type, payload }),
      })
      if (!res.ok) throw new Error('Failed to send command')
      return res.json() as Promise<CommandGroupResponse>
    },
  })
}

// ── Test Expression ───────────────────────────────────────────────────────

export function useTestExpression() {
  return useMutation({
    mutationFn: async ({
      application,
      environment,
      expression,
      language,
      body,
      target,
    }: {
      application: string
      environment: string
      expression: string
      language: string
      body: string
      target: string
    }) => {
      const res = await authFetch(
        `/environments/${encodeURIComponent(environment)}/apps/${encodeURIComponent(application)}/config/test-expression`,
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ expression, language, body, target }),
        },
      )
      if (!res.ok) {
        if (res.status === 404) throw new Error('No live agent available')
        if (res.status === 504) throw new Error('Expression test timed out')
        throw new Error('Failed to test expression')
      }
      return res.json() as Promise<{ result?: string; error?: string }>
    },
  })
}

// ── Route Control ────────────────────────────────────────────────────────

export function useSendRouteCommand() {
  return useMutation({
    mutationFn: async ({ application, action, routeId, environment }: {
      application: string
      action: 'start' | 'stop' | 'suspend' | 'resume'
      routeId: string
      environment?: string
    }) => {
      const envParam = environment ? `?environment=${encodeURIComponent(environment)}` : ''
      const res = await authFetch(`/agents/groups/${encodeURIComponent(application)}/commands${envParam}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ type: 'route-control', payload: { routeId, action, nonce: crypto.randomUUID() } }),
      })
      if (!res.ok) throw new Error('Failed to send route command')
      return res.json() as Promise<CommandGroupResponse>
    },
  })
}

// ── Replay Exchange ───────────────────────────────────────────────────────

export interface ReplayResult {
  status: string
  message: string
  data?: string
}

export function useReplayExchange() {
  return useMutation({
    mutationFn: async ({
      agentId,
      routeId,
      headers,
      body,
      originalExchangeId,
    }: {
      agentId: string
      routeId: string
      headers?: Record<string, string>
      body: string
      originalExchangeId?: string
    }): Promise<ReplayResult> => {
      const res = await authFetch(`/agents/${encodeURIComponent(agentId)}/replay`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ routeId, body, headers: headers ?? {}, originalExchangeId }),
      })
      if (!res.ok) {
        if (res.status === 404) throw new Error('Agent not found')
        if (res.status === 504) throw new Error('Replay timed out — agent did not respond')
        throw new Error('Failed to send replay command')
      }
      return res.json() as Promise<ReplayResult>
    },
  })
}
