import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import type { components } from '../schema';
import { apiClient, useSelectedEnv } from './alertMeta';

export type AlertDto = components['schemas']['AlertDto'];
export type UnreadCountResponse = components['schemas']['UnreadCountResponse'];

type AlertState = NonNullable<AlertDto['state']>;
type AlertSeverity = NonNullable<AlertDto['severity']>;

export interface AlertsFilter {
  state?: AlertState | AlertState[];
  severity?: AlertSeverity | AlertSeverity[];
  acked?: boolean;
  read?: boolean;
  ruleId?: string;
  limit?: number;
}

function toArray<T>(v: T | T[] | undefined): T[] | undefined {
  if (v === undefined) return undefined;
  return Array.isArray(v) ? v : [v];
}

// NOTE ON TYPES: the generated OpenAPI schema for env-scoped alert endpoints
// emits `path?: never` plus a `query.env: Environment` parameter because the
// server resolves the env via the `@EnvPath` argument resolver, which the
// OpenAPI scanner does not recognise as a path variable. At runtime the URL
// template `{envSlug}` is substituted from `params.path.envSlug` by
// openapi-fetch regardless of what the TS types say; we therefore cast the
// call options to `any` to bypass the generated type oddity.

/** List alert instances in the current env. Polls every 30s (pauses in background).
 *
 * State + severity filters are server-side (`state=FIRING&state=ACKNOWLEDGED&severity=CRITICAL`).
 * `ruleId` is not a backend param and is still applied via react-query `select`.
 * Each unique (state, severity) combo gets its own cache entry so the server
 * honors the filter and stays as the source of truth.
 */
export function useAlerts(filter: AlertsFilter = {}) {
  const env = useSelectedEnv();
  const fetchLimit = Math.min(filter.limit ?? 200, 200);
  const stateArr = toArray(filter.state);
  const severityArr = toArray(filter.severity);
  const ruleIdFilter = filter.ruleId;

  // Stable, serialisable key — arrays must be sorted so order doesn't create cache misses.
  const stateKey = stateArr ? [...stateArr].sort() : null;
  const severityKey = severityArr ? [...severityArr].sort() : null;

  return useQuery({
    queryKey: ['alerts', env, 'list', fetchLimit, stateKey, severityKey, filter.acked ?? null, filter.read ?? null],
    enabled: !!env,
    refetchInterval: 30_000,
    refetchIntervalInBackground: false,
    queryFn: async () => {
      if (!env) throw new Error('no env');
      const query: Record<string, unknown> = { limit: fetchLimit };
      if (stateArr && stateArr.length > 0) query.state = stateArr;
      if (severityArr && severityArr.length > 0) query.severity = severityArr;
      if (filter.acked !== undefined) query.acked = filter.acked;
      if (filter.read !== undefined) query.read = filter.read;
      const { data, error } = await apiClient.GET(
        '/environments/{envSlug}/alerts',
        {
          params: {
            path: { envSlug: env },
            query,
          },
        } as any,
      );
      if (error) throw error;
      return data as AlertDto[];
    },
    select: (all) => ruleIdFilter ? all.filter((a) => a.ruleId === ruleIdFilter) : all,
  });
}

/** Fetch a single alert instance by id. */
export function useAlert(id: string | undefined) {
  const env = useSelectedEnv();
  return useQuery({
    queryKey: ['alerts', env, 'detail', id],
    enabled: !!env && !!id,
    queryFn: async () => {
      if (!env || !id) throw new Error('no env/id');
      const { data, error } = await apiClient.GET(
        '/environments/{envSlug}/alerts/{id}',
        {
          params: { path: { envSlug: env, id } },
        } as any,
      );
      if (error) throw error;
      return data as AlertDto;
    },
  });
}

/** Unread alert count for the current env. Polls every 30s (pauses in background). */
export function useUnreadCount() {
  const env = useSelectedEnv();
  return useQuery({
    queryKey: ['alerts', env, 'unread-count'],
    enabled: !!env,
    refetchInterval: 30_000,
    refetchIntervalInBackground: false,
    queryFn: async () => {
      if (!env) throw new Error('no env');
      const { data, error } = await apiClient.GET(
        '/environments/{envSlug}/alerts/unread-count',
        {
          params: { path: { envSlug: env } },
        } as any,
      );
      if (error) throw error;
      return data as UnreadCountResponse;
    },
  });
}

/** Acknowledge a single alert instance. */
export function useAckAlert() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (id: string) => {
      if (!env) throw new Error('no env');
      const { data, error } = await apiClient.POST(
        '/environments/{envSlug}/alerts/{id}/ack',
        {
          params: { path: { envSlug: env, id } },
        } as any,
      );
      if (error) throw error;
      return data as AlertDto;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['alerts', env] });
    },
  });
}

/** Mark a single alert instance as read (inbox semantics). */
export function useMarkAlertRead() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (id: string) => {
      if (!env) throw new Error('no env');
      const { error } = await apiClient.POST(
        '/environments/{envSlug}/alerts/{id}/read',
        {
          params: { path: { envSlug: env, id } },
        } as any,
      );
      if (error) throw error;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['alerts', env] });
      qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
    },
  });
}

/** Mark a batch of alert instances as read. */
export function useBulkReadAlerts() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (ids: string[]) => {
      if (!env) throw new Error('no env');
      const { error } = await apiClient.POST(
        '/environments/{envSlug}/alerts/bulk-read',
        {
          params: { path: { envSlug: env } },
          body: { instanceIds: ids },
        } as any,
      );
      if (error) throw error;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['alerts', env] });
      qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
    },
  });
}

/** Acknowledge a batch of alert instances. */
export function useBulkAckAlerts() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (ids: string[]) => {
      if (!env) throw new Error('no env');
      const { error } = await apiClient.POST(
        '/environments/{envSlug}/alerts/bulk-ack',
        { params: { path: { envSlug: env } }, body: { instanceIds: ids } } as any,
      );
      if (error) throw error;
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['alerts', env] }),
  });
}

/** Delete (soft) a single alert instance. */
export function useDeleteAlert() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (id: string) => {
      if (!env) throw new Error('no env');
      const { error } = await apiClient.DELETE(
        '/environments/{envSlug}/alerts/{id}',
        { params: { path: { envSlug: env, id } } } as any,
      );
      if (error) throw error;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['alerts', env] });
      qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
    },
  });
}

/** Delete (soft) a batch of alert instances. */
export function useBulkDeleteAlerts() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (ids: string[]) => {
      if (!env) throw new Error('no env');
      const { error } = await apiClient.POST(
        '/environments/{envSlug}/alerts/bulk-delete',
        { params: { path: { envSlug: env } }, body: { instanceIds: ids } } as any,
      );
      if (error) throw error;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['alerts', env] });
      qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
    },
  });
}

/** Restore a soft-deleted alert instance. */
export function useRestoreAlert() {
  const env = useSelectedEnv();
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (id: string) => {
      if (!env) throw new Error('no env');
      const { error } = await apiClient.POST(
        '/environments/{envSlug}/alerts/{id}/restore',
        { params: { path: { envSlug: env, id } } } as any,
      );
      if (error) throw error;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['alerts', env] });
      qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
    },
  });
}
