/**
 * Energy / Modbus hooks — typed TanStack Query wrappers for /api/modbus/*.
 *
 * All write operations go through the typed apiClient (openapi-fetch), which
 * injects CSRF via the csrfMiddleware already wired into the client.
 *
 * Query-key conventions:
 *   ['modbus-devices']                     — device list
 *   ['modbus-device', uuid]                — single device
 *   ['modbus-profiles']                    — profile list (rarely changes)
 *   ['modbus-latest', uuid]                — latest reading per device
 *   ['modbus-metrics', uuid]               — profile metric metadata per device
 *   ['modbus-readings', uuid, params]      — time-range readings per device
 *
 * On success, mutations invalidate the device list so the UI refreshes.
 */

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import apiClient from '../api/client'
import type { components } from '../api/schema.d.ts'

// ---------------------------------------------------------------------------
// Re-exported types for consumers
// ---------------------------------------------------------------------------

export type ModbusDevice = components['schemas']['ModbusDeviceResponse']
export type ModbusDeviceCreate = components['schemas']['ModbusDeviceCreate']
export type ModbusDeviceUpdate = components['schemas']['ModbusDeviceUpdate']
export type ProfileSummary = components['schemas']['ProfileSummary']
export type ModbusTestReadResponse = components['schemas']['ModbusTestReadResponse']
export type ModbusLatestResponse = components['schemas']['ModbusLatestResponse']
export type ModbusMetricsResponse = components['schemas']['ModbusMetricsResponse']
export type MetricInfo = components['schemas']['MetricInfo']
export type ModbusReadingResponse = components['schemas']['ModbusReadingResponse']
export type ModbusReadingsResponse = components['schemas']['ModbusReadingsResponse']

// ---------------------------------------------------------------------------
// Reading query params
// ---------------------------------------------------------------------------

export interface ReadingsQueryParams {
  /**
   * How many milliseconds of history to show.  When provided the queryFn
   * computes end=now() and start=now()-spanMs on every invocation, so
   * refetchInterval-triggered re-fetches always use a rolling window.
   * Mutually exclusive with `start`/`end`.
   */
  spanMs?: number
  start?: string | null
  end?: string | null
  /** Capped server-side; default max is 1000 to avoid pulling full history. */
  limit?: number
}

// ---------------------------------------------------------------------------
// Query: list all devices
// ---------------------------------------------------------------------------

export function useDevices() {
  return useQuery({
    queryKey: ['modbus-devices'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/modbus/devices')
      return res.data
    },
  })
}

// ---------------------------------------------------------------------------
// Query: list available profiles
// ---------------------------------------------------------------------------

export function useProfiles() {
  return useQuery({
    queryKey: ['modbus-profiles'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/modbus/profiles')
      return res.data
    },
    // Profiles are static — 5 min stale time.
    staleTime: 5 * 60 * 1000,
  })
}

// ---------------------------------------------------------------------------
// Mutation: create device
// ---------------------------------------------------------------------------

export function useCreateDevice() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: ModbusDeviceCreate) =>
      apiClient.POST('/api/modbus/devices', { body }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['modbus-devices'] }),
  })
}

// ---------------------------------------------------------------------------
// Mutation: update (PATCH) device
// ---------------------------------------------------------------------------

export function useUpdateDevice() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ uuid, body }: { uuid: string; body: ModbusDeviceUpdate }) =>
      apiClient.PATCH('/api/modbus/devices/{uuid}', {
        params: { path: { uuid } },
        body,
      }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['modbus-devices'] }),
  })
}

// ---------------------------------------------------------------------------
// Mutation: delete device
// ---------------------------------------------------------------------------

export interface DeleteDeviceParams {
  uuid: string
  /** When true, perform a cascade delete (removes readings + expose toggles). */
  cascade?: boolean
}

export function useDeleteDevice() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ uuid, cascade }: DeleteDeviceParams) =>
      apiClient.DELETE('/api/modbus/devices/{uuid}', {
        params: {
          path: { uuid },
          ...(cascade ? { query: { cascade: true } } : {}),
        },
      }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['modbus-devices'] }),
  })
}

// ---------------------------------------------------------------------------
// Mutation: test-read device (POST /devices/{uuid}/test, does NOT persist)
// ---------------------------------------------------------------------------

export function useTestReadDevice() {
  return useMutation({
    mutationFn: (uuid: string) =>
      apiClient.POST('/api/modbus/devices/{uuid}/test', {
        params: { path: { uuid } },
      }),
  })
}

// ---------------------------------------------------------------------------
// Query options shared by auto-refresh–capable queries
// ---------------------------------------------------------------------------

export interface AutoRefreshOptions {
  /**
   * When provided, TanStack Query will automatically re-fetch this query at
   * this interval (milliseconds).  Pass `undefined` to disable auto-refresh.
   * Minimum enforced value: 2 000 ms.
   */
  refetchIntervalMs?: number
}

// ---------------------------------------------------------------------------
// Query: latest reading for a device
// ---------------------------------------------------------------------------

export function useLatestReading(uuid: string, options?: AutoRefreshOptions) {
  const refetchInterval = options?.refetchIntervalMs != null
    ? Math.max(2_000, options.refetchIntervalMs)
    : undefined

  return useQuery({
    queryKey: ['modbus-latest', uuid],
    queryFn: async () => {
      const res = await apiClient.GET('/api/modbus/devices/{uuid}/latest', {
        params: { path: { uuid } },
      })
      return res.data
    },
    refetchInterval,
  })
}

// ---------------------------------------------------------------------------
// Query: profile metric metadata for a device (label/unit per key)
// ---------------------------------------------------------------------------

export function useMetrics(uuid: string) {
  return useQuery({
    queryKey: ['modbus-metrics', uuid],
    queryFn: async () => {
      const res = await apiClient.GET('/api/modbus/devices/{uuid}/metrics', {
        params: { path: { uuid } },
      })
      return res.data
    },
    // Metrics are static (tied to profile version); 5 min stale time.
    staleTime: 5 * 60 * 1000,
  })
}

// ===========================================================================
// Energy / Pricing hooks — typed TanStack Query wrappers for /api/energy/*.
//
// Query-key conventions:
//   ['energy-contracts']                  — contract list
//   ['energy-contract', id]               — single contract with versions
//   ['energy-profiles']                   — pricing profile structures
//   ['energy-prices', start, end]         — price curve
//   ['energy-costs', start, end, limit]   — cost periods
//   ['energy-costs-summary', start, end]  — summary
//   ['dsmr-latest']                       — DSMR latest
// ===========================================================================

// Re-exported energy types for consumers
export type MeterResponse = components['schemas']['MeterResponse']
export type MeterListResponse = components['schemas']['MeterListResponse']
export type MeterDeclareRequest = components['schemas']['MeterDeclareRequest']
export type MeterPatchRequest = components['schemas']['MeterPatchRequest']
export type MeterReason = components['schemas']['MeterReason']

export type ContractResponse = components['schemas']['ContractResponse']
export type ContractDetailResponse = components['schemas']['ContractDetailResponse']
export type ContractVersionResponse = components['schemas']['ContractVersionResponse']
export type ContractListResponse = components['schemas']['ContractListResponse']
export type ContractCreate = components['schemas']['ContractCreate']
export type ContractPatch = components['schemas']['ContractPatch']
export type VersionCreate = components['schemas']['VersionCreate']
export type ProfilesResponse = components['schemas']['ProfilesResponse']
export type PricesResponse = components['schemas']['PricesResponse']
export type PricePointSchema = components['schemas']['PricePointSchema']
export type ManualTariffSchema = components['schemas']['ManualTariffSchema']
export type CostsResponse = components['schemas']['CostsResponse']
export type CostPeriodSchema = components['schemas']['CostPeriodSchema']
export type SummaryResponse = components['schemas']['SummaryResponse']
export type DsmrLatestResponse = components['schemas']['DsmrLatestResponse']
export type TibberTestResponse = components['schemas']['TibberTestResponse']
export type TibberTestPriceSchema = components['schemas']['TibberTestPriceSchema']

// ---------------------------------------------------------------------------
// Query: list all energy contracts
// ---------------------------------------------------------------------------

export function useContracts() {
  return useQuery({
    queryKey: ['energy-contracts'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/contracts')
      return res.data
    },
  })
}

// ---------------------------------------------------------------------------
// Query: single contract with full version history
// ---------------------------------------------------------------------------

export function useContractDetail(id: number) {
  return useQuery({
    queryKey: ['energy-contract', id],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/contracts/{contract_id}', {
        params: { path: { contract_id: id } },
      })
      return res.data
    },
    enabled: !!id,
  })
}

// ---------------------------------------------------------------------------
// Query: energy pricing profile structures
// ---------------------------------------------------------------------------

export function useEnergyProfiles() {
  return useQuery({
    queryKey: ['energy-profiles'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/profiles')
      return res.data
    },
    staleTime: 5 * 60 * 1000,
  })
}

// ---------------------------------------------------------------------------
// Mutation: create energy contract
// ---------------------------------------------------------------------------

export function useCreateContract() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: ContractCreate) =>
      apiClient.POST('/api/energy/contracts', { body }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['energy-contracts'] }),
  })
}

// ---------------------------------------------------------------------------
// Mutation: update (PATCH) energy contract
// ---------------------------------------------------------------------------

export function useUpdateContract() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: number; body: ContractPatch }) =>
      apiClient.PATCH('/api/energy/contracts/{contract_id}', {
        params: { path: { contract_id: id } },
        body,
      }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['energy-contracts'] }),
  })
}

// ---------------------------------------------------------------------------
// Mutation: add a new version to an existing contract
// ---------------------------------------------------------------------------

export function useAddContractVersion() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: number; body: VersionCreate }) =>
      apiClient.POST('/api/energy/contracts/{contract_id}/versions', {
        params: { path: { contract_id: id } },
        body,
      }),
    onSuccess: (_data, vars) => {
      void qc.invalidateQueries({ queryKey: ['energy-contracts'] })
      void qc.invalidateQueries({ queryKey: ['energy-contract', vars.id] })
    },
  })
}

// ---------------------------------------------------------------------------
// Query: energy price curve (Tibber 15-min / manual tariff)
// ---------------------------------------------------------------------------

export function useEnergyPrices(start?: string, end?: string) {
  return useQuery({
    queryKey: ['energy-prices', start, end],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/prices', {
        params: {
          query: {
            ...(start ? { start } : {}),
            ...(end ? { end } : {}),
          },
        },
      })
      return res.data
    },
  })
}

// ---------------------------------------------------------------------------
// Query: cost periods
// ---------------------------------------------------------------------------

export function useEnergyCosts(start?: string, end?: string, limit?: number) {
  return useQuery({
    queryKey: ['energy-costs', start, end, limit],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/costs', {
        params: {
          query: {
            ...(start ? { start } : {}),
            ...(end ? { end } : {}),
            ...(limit != null ? { limit } : {}),
          },
        },
      })
      return res.data
    },
  })
}

// ---------------------------------------------------------------------------
// Query: cost summary
// ---------------------------------------------------------------------------

export function useEnergyCostSummary(start?: string, end?: string) {
  return useQuery({
    queryKey: ['energy-costs-summary', start, end],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/costs/summary', {
        params: {
          query: {
            ...(start ? { start } : {}),
            ...(end ? { end } : {}),
          },
        },
      })
      return res.data
    },
  })
}

// ---------------------------------------------------------------------------
// Query: DSMR latest reading
// ---------------------------------------------------------------------------

export function useDsmrLatest(options?: AutoRefreshOptions) {
  const refetchInterval = options?.refetchIntervalMs != null
    ? Math.max(2_000, options.refetchIntervalMs)
    : undefined

  return useQuery({
    queryKey: ['dsmr-latest'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/dsmr/latest')
      return res.data
    },
    refetchInterval,
  })
}

// ---------------------------------------------------------------------------
// Mutation: recompute costs
// ---------------------------------------------------------------------------

export function useRecomputeCosts() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ start, end }: { start?: string; end?: string } = {}) => {
      // The schema marks start/end as required, but the backend accepts them as
      // optional query params; we spread only defined values.
      const query = {
        ...(start ? { start } : {}),
        ...(end ? { end } : {}),
      } as { start: string; end: string }
      return apiClient.POST('/api/energy/costs/recompute', {
        params: { query },
      })
    },
    onSuccess: () => {
      void qc.invalidateQueries({ queryKey: ['energy-costs'] })
      void qc.invalidateQueries({ queryKey: ['energy-costs-summary'] })
    },
  })
}

// ===========================================================================
// Meter hooks — typed TanStack Query wrappers for /api/energy/meters.
//
// Query-key conventions:
//   ['energy-meters']          — meter list
// ===========================================================================

// ---------------------------------------------------------------------------
// Query: list all meter epochs
// ---------------------------------------------------------------------------

export function useMeters() {
  return useQuery({
    queryKey: ['energy-meters'],
    queryFn: async () => {
      const res = await apiClient.GET('/api/energy/meters')
      return res.data
    },
  })
}

// ---------------------------------------------------------------------------
// Mutation: declare a new meter epoch (swap / home move / initial)
// ---------------------------------------------------------------------------

export function useDeclareMeter() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: MeterDeclareRequest) =>
      apiClient.POST('/api/energy/meters', { body }),
    onSuccess: () => {
      void qc.invalidateQueries({ queryKey: ['energy-meters'] })
      // Invalidate cost-related queries: a new meter may trigger recompute server-side.
      void qc.invalidateQueries({ queryKey: ['energy-costs'] })
      void qc.invalidateQueries({ queryKey: ['energy-costs-summary'] })
    },
  })
}

// ---------------------------------------------------------------------------
// Mutation: update (PATCH) a meter epoch (label / note / started_at)
// ---------------------------------------------------------------------------

export function useUpdateMeter() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: number; body: MeterPatchRequest }) =>
      apiClient.PATCH('/api/energy/meters/{meter_id}', {
        params: { path: { meter_id: id } },
        body,
      }),
    onSuccess: () => {
      void qc.invalidateQueries({ queryKey: ['energy-meters'] })
      // Retroactive started_at correction triggers recompute server-side.
      void qc.invalidateQueries({ queryKey: ['energy-costs'] })
      void qc.invalidateQueries({ queryKey: ['energy-costs-summary'] })
    },
  })
}

// ---------------------------------------------------------------------------
// Query: time-range readings for a device (window + limit — never full-table)
// ---------------------------------------------------------------------------

/** Hard upper-bound on readings fetched; prevents accidental full-table pulls. */
const READINGS_MAX_LIMIT = 1000

export function useReadings(
  uuid: string,
  params: ReadingsQueryParams,
  options?: AutoRefreshOptions,
) {
  const { spanMs, start, end, limit } = params
  const effectiveLimit = Math.min(limit ?? READINGS_MAX_LIMIT, READINGS_MAX_LIMIT)

  const refetchInterval = options?.refetchIntervalMs != null
    ? Math.max(2_000, options.refetchIntervalMs)
    : undefined

  // When spanMs is provided, the query key uses the stable span value (not
  // absolute timestamps), so switching presets triggers a fresh fetch while
  // refetchInterval-triggered re-fetches reuse the cached key and run the
  // queryFn again — computing end=now() each time, giving a rolling window.
  const queryKey = spanMs != null
    ? ['modbus-readings', uuid, { spanMs, limit: effectiveLimit }]
    : ['modbus-readings', uuid, { start, end, limit: effectiveLimit }]

  return useQuery({
    queryKey,
    queryFn: async () => {
      let resolvedStart = start
      let resolvedEnd = end

      if (spanMs != null) {
        const now = new Date()
        resolvedEnd = now.toISOString()
        resolvedStart = new Date(now.getTime() - spanMs).toISOString()
      }

      const res = await apiClient.GET('/api/modbus/devices/{uuid}/readings', {
        params: {
          path: { uuid },
          query: {
            ...(resolvedStart ? { start: resolvedStart } : {}),
            ...(resolvedEnd ? { end: resolvedEnd } : {}),
            limit: effectiveLimit,
          },
        },
      })
      return res.data
    },
    // Enabled only when we have valid uuid; start/end may be null (full window).
    enabled: !!uuid,
    refetchInterval,
  })
}
