import type { BoardSummaryEntry, ListWithCards, TrelloCard, TrelloList } from './types.js';

export class TrelloApiError extends Error {
  constructor(
    message: string,
    public readonly statusCode: number,
  ) {
    super(message);
    this.name = 'TrelloApiError';
  }
}

const LIST_PATTERNS = {
  todo: /\b(to[\s\-_]?do|backlog|todo|open|new)\b/i,
  doing: /\b(doing|in[\s\-_]?progress|wip|active|current)\b/i,
  done: /\b(done|complete|finished|closed|shipped|released)\b/i,
} as const;

export type ListCategory = keyof typeof LIST_PATTERNS;

export class TrelloClient {
  private readonly base = 'https://api.trello.com/1';

  constructor(
    private readonly apiKey: string,
    private readonly token: string,
    private readonly boardId: string,
  ) {}

  private auth(): string {
    return `key=${this.apiKey}&token=${this.token}`;
  }

  private async request<T>(path: string): Promise<T> {
    const sep = path.includes('?') ? '&' : '?';
    const res = await fetch(`${this.base}${path}${sep}${this.auth()}`);
    if (!res.ok) {
      throw new TrelloApiError(`Trello API error: ${res.statusText}`, res.status);
    }
    return res.json() as Promise<T>;
  }

  private async post<T>(path: string, body: Record<string, string>): Promise<T> {
    const params = new URLSearchParams({ ...body, key: this.apiKey, token: this.token });
    const res = await fetch(`${this.base}${path}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: params.toString(),
    });
    if (!res.ok) {
      throw new TrelloApiError(`Trello API error: ${res.statusText}`, res.status);
    }
    return res.json() as Promise<T>;
  }

  async getLists(): Promise<TrelloList[]> {
    return this.request<TrelloList[]>(`/boards/${this.boardId}/lists?filter=open`);
  }

  async getCards(): Promise<TrelloCard[]> {
    return this.request<TrelloCard[]>(`/boards/${this.boardId}/cards?filter=open`);
  }

  async getCardsByListName(category: ListCategory): Promise<ListWithCards | null> {
    const [lists, cards] = await Promise.all([this.getLists(), this.getCards()]);
    const pattern = LIST_PATTERNS[category];
    const list = lists.find((l) => pattern.test(l.name));
    if (!list) return null;
    const listCards = cards
      .filter((c) => c.idList === list.id)
      .sort(
        (a, b) => new Date(b.dateLastActivity).getTime() - new Date(a.dateLastActivity).getTime(),
      );
    return { list, cards: listCards };
  }

  async createCard(name: string, listId: string): Promise<TrelloCard> {
    return this.post<TrelloCard>('/cards', { name, idList: listId });
  }

  async getBoardSummary(): Promise<BoardSummaryEntry[]> {
    const [lists, cards] = await Promise.all([this.getLists(), this.getCards()]);
    return lists.map((list) => ({
      list,
      count: cards.filter((c) => c.idList === list.id).length,
    }));
  }

  /** Resolves the list ID to use for /btw. Uses TRELLO_BTW_LIST_ID env if set, otherwise first open list. */
  async resolveBtwListId(overrideId?: string): Promise<string> {
    if (overrideId) return overrideId;
    const lists = await this.getLists();
    const first = lists.sort((a, b) => a.pos - b.pos)[0];
    if (!first) throw new TrelloApiError('No open lists found on board', 404);
    return first.id;
  }
}
