import { useCallback, useEffect, useRef, useState } from 'react';
import { ApiError, request } from './client';
import { getDashboardHealth, getDashboardVersion, type DashboardVersion } from './dashboard';
import { getMediaSources, type MediaSource } from './libraries';
import { getFFmpegProfiles, getFillerPresets, getWatermarks, type FFmpegProfile, type FillerPreset, type Watermark } from './pickers';
import type { components } from './generated/v1';

export type FfmpegSettings = components['schemas']['FFmpegSettingsResponseModel'];
export type UpdateFfmpegSettingsRequest = components['schemas']['UpdateFFmpegSettingsRequest'];
export type PlayoutSettings = components['schemas']['PlayoutSettingsResponseModel'];
export type UpdatePlayoutSettingsRequest = components['schemas']['UpdatePlayoutSettingsRequest'];
export type XmltvSettings = components['schemas']['XmltvSettingsResponseModel'];
export type UpdateXmltvSettingsRequest = components['schemas']['UpdateXmltvSettingsRequest'];
export type ScannerSettings = components['schemas']['ScannerSettingsResponseModel'];
export type UpdateScannerSettingsRequest = components['schemas']['UpdateScannerSettingsRequest'];
export type LoggingSettings = components['schemas']['LoggingSettingsResponseModel'];
export type UpdateLoggingSettingsRequest = components['schemas']['UpdateLoggingSettingsRequest'];
export type UiSettings = components['schemas']['UiSettingsResponseModel'];
export type UpdateUiSettingsRequest = components['schemas']['UpdateUiSettingsRequest'];
export type HdhrSettings = components['schemas']['HdhrSettingsResponseModel'];
export type UpdateHdhrSettingsRequest = components['schemas']['UpdateHdhrSettingsRequest'];
export type IptvSettings = components['schemas']['IptvSettingsResponseModel'];
export type UpdateIptvSettingsRequest = components['schemas']['UpdateIptvSettingsRequest'];
export type Resolution = components['schemas']['ResolutionResponseModel'];
export type CreateResolutionRequest = components['schemas']['CreateResolutionRequest'];
export type LogEventLevel = components['schemas']['LogEventLevel'];
export type XmltvTimeZone = components['schemas']['XmltvTimeZone'];
export type XmltvBlockBehavior = components['schemas']['XmltvBlockBehavior'];
export type OutputFormatKind = components['schemas']['OutputFormatKind'];
export type HealthCheck = components['schemas']['HealthCheckResponseModel'];

export function getFfmpegSettings(): Promise<FfmpegSettings> {
  return request<FfmpegSettings>('/api/v1/settings/ffmpeg');
}

export function updateFfmpegSettings(body: UpdateFfmpegSettingsRequest): Promise<FfmpegSettings> {
  return request<FfmpegSettings>('/api/v1/settings/ffmpeg', { body, method: 'PUT' });
}

export function getPlayoutSettings(): Promise<PlayoutSettings> {
  return request<PlayoutSettings>('/api/v1/settings/playout');
}

export function updatePlayoutSettings(body: UpdatePlayoutSettingsRequest): Promise<PlayoutSettings> {
  return request<PlayoutSettings>('/api/v1/settings/playout', { body, method: 'PUT' });
}

export function getXmltvSettings(): Promise<XmltvSettings> {
  return request<XmltvSettings>('/api/v1/settings/xmltv');
}

export function updateXmltvSettings(body: UpdateXmltvSettingsRequest): Promise<XmltvSettings> {
  return request<XmltvSettings>('/api/v1/settings/xmltv', { body, method: 'PUT' });
}

export function getScannerSettings(): Promise<ScannerSettings> {
  return request<ScannerSettings>('/api/v1/settings/scanner');
}

export function updateScannerSettings(body: UpdateScannerSettingsRequest): Promise<ScannerSettings> {
  return request<ScannerSettings>('/api/v1/settings/scanner', { body, method: 'PUT' });
}

export function getLoggingSettings(): Promise<LoggingSettings> {
  return request<LoggingSettings>('/api/v1/settings/logging');
}

export function updateLoggingSettings(body: UpdateLoggingSettingsRequest): Promise<LoggingSettings> {
  return request<LoggingSettings>('/api/v1/settings/logging', { body, method: 'PUT' });
}

export function getUiSettings(): Promise<UiSettings> {
  return request<UiSettings>('/api/v1/settings/ui');
}

export function updateUiSettings(body: UpdateUiSettingsRequest): Promise<UiSettings> {
  return request<UiSettings>('/api/v1/settings/ui', { body, method: 'PUT' });
}

export function getHdhrSettings(): Promise<HdhrSettings> {
  return request<HdhrSettings>('/api/v1/settings/hdhr');
}

export function updateHdhrSettings(body: UpdateHdhrSettingsRequest): Promise<HdhrSettings> {
  return request<HdhrSettings>('/api/v1/settings/hdhr', { body, method: 'PUT' });
}

export function getIptvSettings(): Promise<IptvSettings> {
  return request<IptvSettings>('/api/v1/settings/iptv');
}

export function updateIptvSettings(body: UpdateIptvSettingsRequest): Promise<IptvSettings> {
  return request<IptvSettings>('/api/v1/settings/iptv', { body, method: 'PUT' });
}

export function getResolutions(): Promise<Resolution[]> {
  return request<Resolution[]>('/api/v1/settings/resolutions');
}

export function createResolution(body: CreateResolutionRequest): Promise<Resolution> {
  return request<Resolution>('/api/v1/settings/resolutions', { body, method: 'POST' });
}

export function deleteResolution(id: number): Promise<void> {
  return request<void>(`/api/v1/settings/resolutions/${id}`, { method: 'DELETE' });
}

export interface SettingsScreenData {
  ffmpeg: FfmpegSettings;
  playout: PlayoutSettings;
  xmltv: XmltvSettings;
  scanner: ScannerSettings;
  logging: LoggingSettings;
  ui: UiSettings;
  hdhr: HdhrSettings;
  iptv: IptvSettings;
  resolutions: Resolution[];
  ffmpegProfiles: FFmpegProfile[];
  watermarks: Watermark[];
  fillerPresets: FillerPreset[];
  mediaSources: MediaSource[];
  version: DashboardVersion | null;
  health: HealthCheck[];
  // Per-resource tier-2 failure flags: reference data that failed to load. The screen
  // still renders (with empty/null fallbacks above); the affected card shows an inline
  // "Couldn't load X" note instead of data.
  referenceFailures: ReferenceFailures;
}

export interface ReferenceFailures {
  ffmpegProfiles: boolean;
  fillerPresets: boolean;
  health: boolean;
  mediaSources: boolean;
  version: boolean;
  watermarks: boolean;
}

export type SettingsScreenQueryState =
  | {
      data: SettingsScreenData;
      error: string | null;
      refresh: () => void;
      refreshResolutions: () => Promise<void>;
      status: 'success';
    }
  | { data: null; error: string; refresh: () => void; refreshResolutions: () => Promise<void>; status: 'error' }
  | { data: null; error: null; refresh: () => void; refreshResolutions: () => Promise<void>; status: 'loading' };

type SettingsScreenState =
  | { data: SettingsScreenData; error: string | null; status: 'success' }
  | { data: null; error: string; status: 'error' }
  | { data: null; error: null; status: 'loading' };

// A settled tier-2 (reference-data) result: falls back to `fallback` on failure and
// records the failure so the UI can show an inline note instead of data.
function settled<T>(result: PromiseSettledResult<T>, fallback: T): { failed: boolean; value: T } {
  return result.status === 'fulfilled' ? { failed: false, value: result.value } : { failed: true, value: fallback };
}

async function loadSettingsScreenData(): Promise<SettingsScreenData> {
  // Tier 1 (critical): the settings groups themselves + resolutions. A failure here
  // rejects and produces the screen-level error state (with retry).
  const tier1 = Promise.all([
    getFfmpegSettings(),
    getPlayoutSettings(),
    getXmltvSettings(),
    getScannerSettings(),
    getLoggingSettings(),
    getUiSettings(),
    getHdhrSettings(),
    getIptvSettings(),
    getResolutions()
  ]);

  // Tier 2 (reference data): each settles independently. A failure never blocks the
  // screen - the affected card degrades to an inline "Couldn't load X" note.
  const tier2 = Promise.allSettled([
    getFFmpegProfiles(),
    getWatermarks(),
    getFillerPresets(),
    getMediaSources(),
    getDashboardVersion(),
    getDashboardHealth()
  ] as const);

  const [ffmpeg, playout, xmltv, scanner, logging, ui, hdhr, iptv, resolutions] = await tier1;
  const [profilesResult, watermarksResult, fillerPresetsResult, mediaSourcesResult, versionResult, healthResult] =
    await tier2;

  const ffmpegProfiles = settled<FFmpegProfile[]>(profilesResult, []);
  const watermarks = settled<Watermark[]>(watermarksResult, []);
  const fillerPresets = settled<FillerPreset[]>(fillerPresetsResult, []);
  const mediaSources = settled<MediaSource[]>(mediaSourcesResult, []);
  const version = settled<DashboardVersion | null>(versionResult, null);
  const health = settled<HealthCheck[]>(healthResult, []);

  return {
    ffmpeg,
    ffmpegProfiles: ffmpegProfiles.value,
    fillerPresets: fillerPresets.value,
    health: health.value,
    hdhr,
    iptv,
    logging,
    mediaSources: mediaSources.value,
    playout,
    referenceFailures: {
      ffmpegProfiles: ffmpegProfiles.failed,
      fillerPresets: fillerPresets.failed,
      health: health.failed,
      mediaSources: mediaSources.failed,
      version: version.failed,
      watermarks: watermarks.failed
    },
    resolutions,
    scanner,
    ui,
    version: version.value,
    watermarks: watermarks.value,
    xmltv
  };
}

export function useSettingsScreenQuery(): SettingsScreenQueryState {
  const [state, setState] = useState<SettingsScreenState>({ data: null, error: null, status: 'loading' });
  const activeRef = useRef(true);

  useEffect(() => {
    activeRef.current = true;

    return () => {
      activeRef.current = false;
    };
  }, []);

  const load = useCallback(() => {
    loadSettingsScreenData()
      .then((data) => {
        if (activeRef.current) {
          setState({ data, error: null, status: 'success' });
        }
      })
      .catch((error: unknown) => {
        if (activeRef.current) {
          setState({ data: null, error: messageFromSettingsError(error, 'Unable to load settings'), status: 'error' });
        }
      });
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const refresh = useCallback(() => {
    setState({ data: null, error: null, status: 'loading' });
    load();
  }, [load]);

  const refreshResolutions = useCallback(async () => {
    const resolutions = await getResolutions();

    if (!activeRef.current) {
      return;
    }

    setState((current) => {
      if (current.status !== 'success') {
        return current;
      }

      return { data: { ...current.data, resolutions }, error: current.error, status: 'success' };
    });
  }, []);

  if (state.status === 'success') {
    return { data: state.data, error: state.error, refresh, refreshResolutions, status: 'success' };
  }

  if (state.status === 'error') {
    return { data: null, error: state.error, refresh, refreshResolutions, status: 'error' };
  }

  return { data: null, error: null, refresh, refreshResolutions, status: 'loading' };
}

export function messageFromSettingsError(error: unknown, fallback = 'Unable to load settings'): string {
  if (error instanceof ApiError) {
    return error.detail ?? error.message;
  }

  if (error instanceof Error) {
    return error.message;
  }

  return fallback;
}
