import { rollDice, applyRollMode, naturalD20 } from '@/lib/dice/notation';
import { createRng } from '@/lib/rng';
import { degreeOfSuccess, type Degree } from '@/lib/dice/check';
import { diceRepo } from '@/lib/db/repositories';
import { broadcastGmRoll } from '@/lib/sync/wsSync';
import { useRollStore } from '@/stores/rollStore';
import { useUiStore } from '@/stores/uiStore';
import { useSessionStore } from '@/stores/sessionStore';
import type { SystemId } from '@/lib/rules';

export interface RollOptions {
  expression: string;
  label?: string;
  /** roll against a DC to compute a success degree */
  dc?: number;
  system?: SystemId;
}

/**
 * Roll an expression, show it in the global tray, and persist it to history.
 * Returns the result + degree. Used by the dice page, sheets, and statblocks.
 */
export function rollAndShow(opts: RollOptions): { total: number; degree?: Degree } {
  const mode = useRollStore.getState().mode;
  const expression = applyRollMode(opts.expression, mode);
  const result = rollDice(expression, createRng());
  const campaignId = useUiStore.getState().activeCampaignId ?? undefined;
  const modeTag = mode === 'advantage' ? ' (adv)' : mode === 'disadvantage' ? ' (dis)' : '';
  const label = (opts.label ?? '') + modeTag;

  let degree: Degree | undefined;
  if (opts.dc !== undefined && opts.system) {
    degree = degreeOfSuccess({ total: result.total, dc: opts.dc, system: opts.system, natural: naturalD20(result) });
  }

  useRollStore.getState().push({
    label,
    result,
    ...(opts.dc !== undefined ? { dc: opts.dc } : {}),
    ...(degree ? { degree } : {}),
  });

  void diceRepo.add({
    ...(campaignId ? { campaignId } : {}),
    label,
    expression: opts.expression,
    total: result.total,
    breakdown: result.breakdown,
  });

  // A hosting GM's public rolls go to the table, unless they've toggled "secret".
  if (useSessionStore.getState().role === 'gm' && !useRollStore.getState().secret) {
    broadcastGmRoll(label.trim() || 'Roll', opts.expression, result.total, result.breakdown);
  }

  return { total: result.total, ...(degree ? { degree } : {}) };
}

/** Convenience: roll d20 + modifier, formatting the expression. */
export function rollCheck(modifier: number, label: string, opts?: { dc?: number; system?: SystemId }): void {
  const sign = modifier >= 0 ? `+${modifier}` : `${modifier}`;
  rollAndShow({
    expression: `1d20${modifier === 0 ? '' : sign}`,
    label,
    ...(opts?.dc !== undefined ? { dc: opts.dc } : {}),
    ...(opts?.system ? { system: opts.system } : {}),
  });
}
