import { useCallback, useEffect, useRef, useState } from 'react';
import { ApiError, request } from './client';
import type { components } from './generated/v1';

// ChannelResponseModel.preview.availability is typed as a bare `string` in the generated types
// (ChannelPreviewResponseModel.cs uses a plain C# string, not an enum, to keep the OpenAPI schema
// simple — see the comment there). Hand-maintain the real set of values here so a typo like
// 'ForcedHLSOnly' fails to compile instead of silently disabling a branch forever.
export type ChannelPreviewAvailability = 'Available' | 'ForcedHlsOnly' | 'Unavailable';

export type ChannelSummary = components['schemas']['ChannelResponseModel'];
export type ChannelState = components['schemas']['ChannelStateResponseModel'];
export type Channel = components['schemas']['ChannelDetailResponseModel'];
export type UpdateChannelRequest = components['schemas']['UpdateChannelRequest'];
export type BulkRenumberChannelsRequest = components['schemas']['BulkRenumberChannelsRequest'];
export type BulkMoveChannelsToGroupRequest = components['schemas']['BulkMoveChannelsToGroupRequest'];
export type BulkDeleteChannelsRequest = components['schemas']['BulkDeleteChannelsRequest'];
export type CreateChannelFromLineupRequest = components['schemas']['CreateChannelFromLineupRequest'];
export type CreateChannelFromLineupResponseModel = components['schemas']['CreateChannelFromLineupResponseModel'];
export type CreateChannelFromLineupClearField = components['schemas']['CreateChannelFromLineupClearField'];
export type CreateChannelRequest = components['schemas']['CreateChannelRequest'];

export interface ChannelsScreenData {
  channels: ChannelSummary[];
  channelStates: ChannelState[];
}

export type ChannelsScreenQueryState =
  | { data: ChannelsScreenData; error: null; refresh: () => void; status: 'success' }
  | { data: null; error: string; refresh: () => void; status: 'error' }
  | { data: null; error: null; refresh: () => void; status: 'loading' };

type ChannelsScreenState =
  | { data: ChannelsScreenData; error: null; status: 'success' }
  | { data: null; error: string; status: 'error' }
  | { data: null; error: null; status: 'loading' };

export function getChannels(): Promise<ChannelSummary[]> {
  return request<ChannelSummary[]>('/api/v1/channels');
}

export function getChannelStates(): Promise<ChannelState[]> {
  return request<ChannelState[]>('/api/v1/channels/state');
}

export function getChannelById(channelId: number): Promise<Channel> {
  return request<Channel>(`/api/v1/channels/${channelId}`);
}

export function updateChannel(channelId: number, body: UpdateChannelRequest): Promise<Channel> {
  return request<Channel>(`/api/v1/channels/${channelId}`, {
    body,
    method: 'PUT'
  });
}

export function createChannel(body: CreateChannelRequest): Promise<Channel> {
  return request<Channel>('/api/v1/channels', {
    body,
    method: 'POST'
  });
}

export function getMusicVideoCreditsTemplates(): Promise<string[]> {
  return request<string[]>('/api/v1/channels/music-video-credits-templates');
}

export function getChannelStreamSelectors(): Promise<string[]> {
  return request<string[]>('/api/v1/channels/stream-selectors');
}

export async function getChannelsScreenData(): Promise<ChannelsScreenData> {
  const [channels, channelStates] = await Promise.all([
    getChannels(),
    getChannelStates()
  ]);

  return { channels, channelStates };
}

export function bulkRenumberChannels(body: BulkRenumberChannelsRequest): Promise<void> {
  return request<void>('/api/v1/channels/bulk/renumber', {
    body,
    method: 'POST'
  });
}

export function bulkMoveChannelsToGroup(body: BulkMoveChannelsToGroupRequest): Promise<void> {
  return request<void>('/api/v1/channels/bulk/group', {
    body,
    method: 'POST'
  });
}

export function bulkDeleteChannels(body: BulkDeleteChannelsRequest): Promise<void> {
  return request<void>('/api/v1/channels/bulk/delete', {
    body,
    method: 'POST'
  });
}

export function deleteChannel(channelId: number): Promise<void> {
  return request<void>(`/api/v1/channels/${channelId}`, {
    method: 'DELETE'
  });
}

export function createChannelFromLineup(
  body: CreateChannelFromLineupRequest
): Promise<CreateChannelFromLineupResponseModel> {
  return request<CreateChannelFromLineupResponseModel>('/api/v1/channels/from-lineup', {
    body,
    method: 'POST'
  });
}

export function useChannelsScreenQuery(pollMs = 30000): ChannelsScreenQueryState {
  const [state, setState] = useState<ChannelsScreenState>({
    data: null,
    error: null,
    status: 'loading'
  });
  const activeRef = useRef(true);

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

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

  const load = useCallback((showLoading = true) => {
    if (showLoading) {
      setState({ data: null, error: null, status: 'loading' });
    }

    getChannelsScreenData()
      .then((data) => {
        if (activeRef.current) {
          setState({ data, error: null, status: 'success' });
        }
      })
      .catch((error: unknown) => {
        if (activeRef.current) {
          setState({ data: null, error: messageFromError(error), status: 'error' });
        }
      });
  }, []);

  const loadChannelStates = useCallback(() => {
    getChannelStates()
      .then((channelStates) => {
        if (activeRef.current) {
          setState((current) => {
            if (current.status !== 'success') {
              return current;
            }

            return {
              data: { ...current.data, channelStates },
              error: null,
              status: 'success'
            };
          });
        }
      })
      .catch(() => {
        // Keep the current table visible on background polling failures.
      });
  }, []);

  useEffect(() => {
    const loadForEffect = () => {
      getChannelsScreenData()
        .then((data) => {
          if (activeRef.current) {
            setState({ data, error: null, status: 'success' });
          }
        })
        .catch((error: unknown) => {
          if (activeRef.current) {
            setState({ data: null, error: messageFromError(error), status: 'error' });
          }
        });
    };

    loadForEffect();
    const intervalId = window.setInterval(() => {
      loadChannelStates();
    }, pollMs);

    return () => {
      window.clearInterval(intervalId);
    };
  }, [loadChannelStates, pollMs]);

  const refresh = useCallback(() => {
    load();
  }, [load]);

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

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

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

export function messageFromError(error: unknown): string {
  if (error instanceof ApiError) {
    return error.detail ?? error.message;
  }

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

  return 'Unable to load channels';
}
