import config from './config';
import type {
  ChaosLevel,
  Dispute,
  Game,
  Match,
  Player,
  PlayerId,
  ScoringMode,
} from './types';

async function request<T>(path: string, options?: RequestInit): Promise<T> {
  const res = await fetch(`${config.apiEndpoint}${path}`, {
    headers: { 'Content-Type': 'application/json', 'x-api-key': config.apiKey },
    ...options,
  });
  if (!res.ok) {
    const body = await res.json().catch(() => null);
    throw new Error(body?.msg ?? `API error ${res.status}`);
  }
  return res.json() as Promise<T>;
}

const post = (body: object): RequestInit => ({ method: 'POST', body: JSON.stringify(body) });
const put = (body: object): RequestInit => ({ method: 'PUT', body: JSON.stringify(body) });
const patch = (body: object): RequestInit => ({ method: 'PATCH', body: JSON.stringify(body) });

/** Row shape the server returns for a match; flattened scores, numeric id. */
interface ServerMatch {
  id: number;
  gameId: string;
  p1Score: number;
  p2Score: number;
  winnerId: PlayerId | null;
  note: string | null;
  cheatCheckFlagged: boolean;
  playedAt: string;
  dispute: Dispute | null;
}

function toMatch(row: ServerMatch): Match {
  return {
    id: String(row.id),
    gameId: row.gameId,
    scores: { p1: row.p1Score, p2: row.p2Score },
    winnerId: row.winnerId,
    note: row.note ?? undefined,
    playedAt: row.playedAt,
    cheatCheckFlagged: row.cheatCheckFlagged,
    dispute: row.dispute ?? undefined,
  };
}

export const api = {
  players: () => request<Player[]>('/players'),

  updatePlayer: (id: PlayerId, patchBody: { name?: string; alias?: string }) =>
    request<Player>(`/players/${id}`, patch(patchBody)),

  registerPushToken: (id: PlayerId, token: string) =>
    request<{ ok: boolean }>(`/players/${id}/push-token`, post({ token })),

  games: () => request<Game[]>('/games'),

  addGame: (input: { name: string; emoji: string; scoring: ScoringMode }) =>
    request<Game>('/games', post(input)),

  matches: async () => (await request<ServerMatch[]>('/matches')).map(toMatch),

  logMatch: async (input: {
    gameId: string;
    p1Score: number;
    p2Score: number;
    note?: string;
    loggedBy: PlayerId;
  }) => toMatch(await request<ServerMatch>('/matches', post(input))),

  fileDispute: (matchId: string, statement: string, filedBy: PlayerId) =>
    request<{ ok: boolean }>(`/matches/${matchId}/dispute`, post({ statement, filedBy })),

  resolveDispute: async (matchId: string, status: 'score-stands' | 'overturned') =>
    toMatch(await request<ServerMatch>(`/matches/${matchId}/resolve`, post({ status }))),

  settings: () => request<{ chaos: ChaosLevel }>('/settings'),

  setChaos: (chaos: ChaosLevel) => request<{ chaos: ChaosLevel }>('/settings/chaos', put({ value: chaos })),
};
