import { Card } from "./Card";
import {
  COLOR_GLYPHS,
  COLOR_HEX,
  EXPEDITION_NAMES,
  cardRank,
  isHandshake,
} from "../game/cards";
import { cardsOnBoard } from "../game/engine";
import { DISCARD, PLAY, type GameState, type PlaceType } from "../game/types";

/** Where the hint says to put the card and where to draw from, if hinting. */
export interface BoardHint {
  color: number;
  place: PlaceType;
  drawSource: number;
}

interface BoardProps {
  state: GameState;
  selectedColor: number | null;
  selectedPlace: PlaceType | null;
  canPlay: boolean;
  canDraw: (source: number) => boolean;
  hint: BoardHint | null;
  cardRef: (card: number) => (element: HTMLElement | null) => void;
  onChoosePlace: (place: PlaceType) => void;
  onCancelPlace: () => void;
  onDraw: (source: number) => void;
}

// Compact expedition cards are 124px tall with a default -94px overlap (30px
// visible per card). A full 12-card expedition would need ~454px of stack
// height, far more than the lane zone's available space, so long stacks
// overlap more tightly to stay within the zone (and never cover the discard
// button, score chip, or spill outside the board).
const STACK_ZONE_BUDGET = 190;
const COMPACT_CARD_HEIGHT = 124;

function stackOverlapStyle(count: number): React.CSSProperties {
  if (count <= 1) return {};
  const perCard = (STACK_ZONE_BUDGET - COMPACT_CARD_HEIGHT) / (count - 1);
  const overlap = Math.min(-94, Math.round(perCard - COMPACT_CARD_HEIGHT));
  return { "--stack-overlap": `${overlap}px` } as React.CSSProperties;
}

function expeditionScore(cards: number[]): string | null {
  if (!cards.length) return null;
  const handshakes = cards.filter(isHandshake).length;
  const ranks = cards.reduce((sum, card) => sum + cardRank(card), 0);
  const score = (ranks - 20) * (1 + handshakes) + (cards.length >= 8 ? 20 : 0);
  return `${score >= 0 ? "+" : ""}${score}${handshakes ? ` ×${handshakes + 1}` : ""}`;
}

export function Board({
  state,
  selectedColor,
  selectedPlace,
  canPlay,
  canDraw,
  hint,
  cardRef,
  onChoosePlace,
  onCancelPlace,
  onDraw,
}: BoardProps) {
  return (
    <section className="board" aria-label="Expeditions and discard piles">
      {EXPEDITION_NAMES.map((name, color) => {
        const opponent = cardsOnBoard(state, 1, color);
        const mine = cardsOnBoard(state, 0, color);
        const discard = state.piles[color];
        const playTarget = selectedColor === color && selectedPlace === null && canPlay;
        const discardTarget = selectedColor === color && selectedPlace === null;
        const drawTarget = selectedPlace !== null && canDraw(color + 1);
        const discardChosen = selectedPlace === DISCARD && selectedColor === color;
        const playChosen = selectedPlace === PLAY && selectedColor === color;
        const hintPlay = hint?.place === PLAY && hint.color === color;
        const hintDiscard = hint?.place === DISCARD && hint.color === color;
        const hintDraw = hint?.drawSource === color + 1;
        return (
          <article
            className="lane"
            key={name}
            style={{ "--lane-color": COLOR_HEX[color] } as React.CSSProperties}
          >
            <div className="lane__zone lane__zone--opponent">
              {opponent.length ? (
                <div className="card-stack card-stack--opponent" style={stackOverlapStyle(opponent.length)}>
                  {opponent.map((card) => <Card compact card={card} key={card} innerRef={cardRef(card)} />)}
                </div>
              ) : (
                <span className="lane__ghost">{COLOR_GLYPHS[color]}</span>
              )}
              {expeditionScore(opponent) && <b className="score-chip">{expeditionScore(opponent)}</b>}
            </div>

            <button
              type="button"
              className={`lane__discard ${discardTarget ? "is-target" : ""} ${drawTarget ? "is-draw-target" : ""} ${discardChosen ? "is-chosen" : ""} ${hintDiscard || hintDraw ? "is-hinted" : ""}`}
              onClick={() => drawTarget ? onDraw(color + 1) : discardChosen ? onCancelPlace() : discardTarget ? onChoosePlace(DISCARD) : undefined}
              disabled={!discardTarget && !drawTarget && !discardChosen}
              aria-label={discardChosen ? `Cancel discarding to ${name} pile` : `${name} discard pile`}
            >
              {discard.length ? (
                <Card compact card={discard.at(-1)!} innerRef={cardRef(discard.at(-1)!)} />
              ) : (
                <span className="lane__ghost">{COLOR_GLYPHS[color]}</span>
              )}
              {discard.length > 1 && <b className="pile-count">×{discard.length}</b>}
            </button>
            <span className="lane__name">{name}</span>

            <button
              type="button"
              className={`lane__zone lane__zone--mine ${playTarget ? "is-target" : ""} ${playChosen ? "is-chosen" : ""} ${hintPlay ? "is-hinted" : ""}`}
              onClick={() => playChosen ? onCancelPlace() : playTarget ? onChoosePlace(PLAY) : undefined}
              disabled={!playTarget && !playChosen}
              aria-label={playChosen ? `Cancel playing to your ${name} expedition` : `Your ${name} expedition`}
            >
              {mine.length ? (
                <div className="card-stack card-stack--mine" style={stackOverlapStyle(mine.length)}>
                  {mine.map((card) => <Card compact card={card} key={card} innerRef={cardRef(card)} />)}
                </div>
              ) : (
                <span className="lane__ghost">{COLOR_GLYPHS[color]}</span>
              )}
              {expeditionScore(mine) && <b className="score-chip">{expeditionScore(mine)}</b>}
            </button>
          </article>
        );
      })}
    </section>
  );
}
