import { API_ENDPOINTS } from '@/shared/constants/apiEndpoints';
import { api } from '@/shared/utils/api';
import {
  type SettingDefinition,
  type SettingItem,
  type SettingMetadata,
  type SettingScope,
  isSettingDefinition,
  parseSettingsMap,
} from '@/features/settings/types/settings.types';

export type SettingsRequestContext = {
  scope?: SettingScope;
  departmentId?: string;
  companyId?: string;
};

function buildSettingsQuery(group?: string, ctx?: SettingsRequestContext): string {
  const params = new URLSearchParams();
  if (group) {
    params.set('group', group);
  }
  if (ctx?.scope) {
    params.set('scope', ctx.scope);
  }
  if (ctx?.departmentId) {
    params.set('department_id', ctx.departmentId);
  }
  if (ctx?.companyId) {
    params.set('company_id', ctx.companyId);
  }
  const qs = params.toString();
  return qs ? `?${qs}` : '';
}

function contextPayload(ctx?: SettingsRequestContext): Record<string, string | undefined> {
  return {
    department_id: ctx?.departmentId,
    company_id: ctx?.companyId,
  };
}

export async function fetchSettings(
  group?: string,
  ctx?: SettingsRequestContext,
): Promise<Record<string, SettingItem>> {
  const raw: unknown = await api.get(
    `${API_ENDPOINTS.SETTINGS.LIST}${buildSettingsQuery(group, ctx)}`,
  );
  return parseSettingsMap(raw);
}

export async function fetchPublicSettings(): Promise<Record<string, unknown>> {
  const raw: unknown = await api.get(API_ENDPOINTS.SETTINGS.PUBLIC);

  if (typeof raw === 'object' && raw !== null && 'data' in raw) {
    const data = (raw as { data: unknown }).data;
    if (typeof data === 'object' && data !== null) {
      return data as Record<string, unknown>;
    }
  }

  return typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : {};
}

export async function fetchSetting(key: string): Promise<SettingMetadata & { key: string }> {
  const raw: unknown = await api.get(API_ENDPOINTS.SETTINGS.SHOW(key));

  if (typeof raw === 'object' && raw !== null && 'data' in raw) {
    const data = (raw as { data: unknown }).data;
    if (typeof data === 'object' && data !== null) {
      const entry = data as Record<string, unknown>;
      return {
        key,
        value: entry.value,
        source: entry.source as SettingMetadata['source'],
        scope_id: (entry.scope_id as string | null) ?? null,
      };
    }
  }

  throw new Error('Invalid setting response');
}

export async function updateSetting(
  key: string,
  value: unknown,
  scope: SettingScope = 'department',
  ctx?: SettingsRequestContext,
): Promise<void> {
  await api.put(API_ENDPOINTS.SETTINGS.UPDATE(key), { value, scope, ...contextPayload(ctx) });
}

export async function resetSetting(
  key: string,
  scope: SettingScope = 'department',
  ctx?: SettingsRequestContext,
): Promise<void> {
  await api.post(API_ENDPOINTS.SETTINGS.RESET(key), { scope, ...contextPayload(ctx) });
}

export async function deleteSetting(
  key: string,
  scope: SettingScope = 'department',
  ctx?: SettingsRequestContext,
): Promise<void> {
  await api.delete(API_ENDPOINTS.SETTINGS.DELETE(key), { data: { scope, ...contextPayload(ctx) } });
}

export async function fetchSettingDefinitions(group?: string): Promise<SettingDefinition[]> {
  const query = group ? `?group=${encodeURIComponent(group)}` : '';
  const raw: unknown = await api.get(`${API_ENDPOINTS.SETTING_DEFINITIONS.LIST}${query}`);

  if (Array.isArray(raw) && raw.every(isSettingDefinition)) {
    return raw;
  }

  if (typeof raw === 'object' && raw !== null && 'data' in raw) {
    const data = (raw as { data: unknown }).data;
    if (Array.isArray(data) && data.every(isSettingDefinition)) {
      return data;
    }
  }

  return [];
}

export async function fetchSettingGroups(): Promise<string[]> {
  const raw: unknown = await api.get(API_ENDPOINTS.SETTING_DEFINITIONS.GROUPS);

  if (typeof raw === 'object' && raw !== null && 'data' in raw) {
    const data = (raw as { data: unknown }).data;
    if (Array.isArray(data) && data.every((g) => typeof g === 'string')) {
      return data;
    }
  }

  if (Array.isArray(raw) && raw.every((g) => typeof g === 'string')) {
    return raw;
  }

  return [];
}
