import { defineStore } from 'pinia';
import { ref, computed, watch } from 'vue';
import {
  PARLAY_MIN_LEGS,
  PARLAY_MAX_LEGS,
  canSelectForParlay,
  type ParlayRejectReason,
} from '@thebet365/shared';
import { useAuthStore } from './auth';

export interface SlipItem {
  selectionId: string;
  oddsVersion: string;
  matchId: string;
  matchName: string;
  marketId?: string;
  marketName?: string;
  selectionName: string;
  odds: number;
  marketType: string;
  lineValue?: number | null;
  allowSingle?: boolean;
  allowParlay?: boolean;
}

export type SlipMode = 'single' | 'parlay';
export type ParlaySlipError = ParlayRejectReason | 'MAX_LEGS' | 'SAME_MATCH';

const BET_SLIP_STORAGE_PREFIX = 'player:betSlip:v1';
const DESKTOP_PANEL_EXPANDED_KEY = 'player:desktopBetSlipExpanded:v1';

function readDesktopPanelExpanded() {
  try {
    return localStorage.getItem(DESKTOP_PANEL_EXPANDED_KEY) !== '0';
  } catch {
    return true;
  }
}

function writeDesktopPanelExpanded(expanded: boolean) {
  try {
    localStorage.setItem(DESKTOP_PANEL_EXPANDED_KEY, expanded ? '1' : '0');
  } catch {
    // ignore quota / private mode errors
  }
}

interface BetSlipPersisted {
  singleItem: SlipItem | null;
  singleCartItems: SlipItem[];
  parlayItems: SlipItem[];
  stake: number;
  singleStakes: Record<string, number>;
  mode: SlipMode;
}

function betSlipStorageKey(userId?: string | null) {
  return userId ? `${BET_SLIP_STORAGE_PREFIX}:${userId}` : `${BET_SLIP_STORAGE_PREFIX}:guest`;
}

function isValidSlipItem(item: unknown): item is SlipItem {
  if (!item || typeof item !== 'object') return false;
  const row = item as SlipItem;
  return Boolean(
    row.selectionId &&
      row.oddsVersion &&
      row.matchId &&
      row.matchName &&
      row.selectionName &&
      typeof row.odds === 'number' &&
      Number.isFinite(row.odds),
  );
}

function readPersisted(userId?: string | null): BetSlipPersisted | null {
  try {
    const raw = localStorage.getItem(betSlipStorageKey(userId));
    if (!raw) return null;
    return JSON.parse(raw) as BetSlipPersisted;
  } catch {
    return null;
  }
}

function writePersisted(payload: BetSlipPersisted, userId?: string | null) {
  try {
    localStorage.setItem(betSlipStorageKey(userId), JSON.stringify(payload));
  } catch {
    // ignore quota / private mode errors
  }
}

function removePersisted(userId?: string | null) {
  localStorage.removeItem(betSlipStorageKey(userId));
}

export const useBetSlipStore = defineStore('betSlip', () => {
  const singleItem = ref<SlipItem | null>(null);
  const singleCartItems = ref<SlipItem[]>([]);
  const parlayItems = ref<SlipItem[]>([]);
  const stake = ref<number>(5);
  const singleStakes = ref<Record<string, number>>({});
  const mode = ref<SlipMode>('single');
  const lastParlayError = ref<ParlaySlipError | null>(null);

  const auth = useAuthStore();
  let persistPaused = false;

  function slipSnapshot(): BetSlipPersisted {
    return {
      singleItem: singleItem.value,
      singleCartItems: singleCartItems.value,
      parlayItems: parlayItems.value,
      stake: stake.value,
      singleStakes: singleStakes.value,
      mode: mode.value,
    };
  }

  function hydrateFromStorage(userId?: string | null) {
    persistPaused = true;
    try {
      const saved = readPersisted(userId);
      if (!saved) {
        singleItem.value = null;
        singleCartItems.value = [];
        parlayItems.value = [];
        singleStakes.value = {};
        stake.value = 5;
        mode.value = 'single';
        lastParlayError.value = null;
        return;
      }
      singleItem.value = saved.singleItem && isValidSlipItem(saved.singleItem) ? saved.singleItem : null;
      singleCartItems.value = (saved.singleCartItems ?? []).filter(isValidSlipItem);
      parlayItems.value = (saved.parlayItems ?? []).filter(isValidSlipItem);
      stake.value =
        typeof saved.stake === 'number' && Number.isFinite(saved.stake) && saved.stake > 0
          ? saved.stake
          : 5;
      singleStakes.value =
        saved.singleStakes && typeof saved.singleStakes === 'object' ? { ...saved.singleStakes } : {};
      mode.value = saved.mode === 'parlay' ? 'parlay' : 'single';
      lastParlayError.value = null;
    } finally {
      persistPaused = false;
    }
  }

  function persistToStorage() {
    if (persistPaused) return;
    const userId = auth.user?.id ?? null;
    const snapshot = slipSnapshot();
    const hasItems =
      Boolean(snapshot.singleItem) ||
      snapshot.singleCartItems.length > 0 ||
      snapshot.parlayItems.length > 0;
    if (!hasItems) {
      removePersisted(userId);
      return;
    }
    writePersisted(snapshot, userId);
  }

  hydrateFromStorage(auth.user?.id ?? null);

  watch(
    () => auth.user?.id ?? null,
    (userId) => {
      hydrateFromStorage(userId);
    },
  );

  watch(
    [singleItem, singleCartItems, parlayItems, stake, singleStakes, mode],
    () => {
      persistToStorage();
    },
    { deep: true },
  );

  const items = computed(() =>
    mode.value === 'parlay'
      ? parlayItems.value
      : singleCartItems.value.length
        ? singleCartItems.value
        : singleItem.value
          ? [singleItem.value]
          : [],
  );
  const count = computed(() => items.value.length);
  const parlayCount = computed(() => parlayItems.value.length);
  const singleCount = computed(() =>
    singleCartItems.value.length || (singleItem.value ? 1 : 0),
  );
  const totalCount = computed(() => singleCount.value + parlayCount.value);
  const isParlay = computed(() => mode.value === 'parlay' && parlayItems.value.length >= PARLAY_MIN_LEGS);

  function defaultStakeAmount() {
    const base = Number(stake.value);
    return Number.isFinite(base) && base > 0 ? base : 5;
  }

  function initItemStake(selectionId: string) {
    if (singleStakes.value[selectionId] == null) {
      singleStakes.value = { ...singleStakes.value, [selectionId]: defaultStakeAmount() };
    }
  }

  function getItemStake(selectionId: string) {
    const v = singleStakes.value[selectionId];
    if (v != null && Number.isFinite(v)) return v;
    return defaultStakeAmount();
  }

  function setItemStake(selectionId: string, amount: number) {
    singleStakes.value = { ...singleStakes.value, [selectionId]: amount };
  }

  function clearItemStake(selectionId: string) {
    if (!(selectionId in singleStakes.value)) return;
    const next = { ...singleStakes.value };
    delete next[selectionId];
    singleStakes.value = next;
  }

  function clearAllItemStakes() {
    singleStakes.value = {};
  }

  function parseLineValue(v: number | string | null | undefined): number | null {
    if (v == null || v === '') return null;
    const n = typeof v === 'number' ? v : parseFloat(String(v));
    return Number.isFinite(n) ? n : null;
  }

  function setMode(nextMode: SlipMode) {
    mode.value = nextMode;
    lastParlayError.value = null;
  }

  /** 球赛/详情页：点击任意选项后作为当前单注，打开投注抽屉 */
  function setSingleItem(item: SlipItem) {
    mode.value = 'single';
    singleItem.value = item;
    initItemStake(item.selectionId);
    lastParlayError.value = null;
  }

  function addItem(item: SlipItem) {
    setSingleItem(item);
  }

  /** PC 单关购买列表 */
  function addToSingleCart(item: SlipItem) {
    mode.value = 'single';
    if (singleCartItems.value.some((i) => i.selectionId === item.selectionId)) return;
    singleCartItems.value.unshift(item);
    initItemStake(item.selectionId);
    lastParlayError.value = null;
  }

  function isInSlip(selectionId: string) {
    return (
      singleItem.value?.selectionId === selectionId ||
      singleCartItems.value.some((i) => i.selectionId === selectionId) ||
      parlayItems.value.some((i) => i.selectionId === selectionId)
    );
  }

  /** 串关：必须来自不同赛事，仍按盘口规则过滤不可串关项 */
  function addParlayLeg(item: SlipItem): ParlaySlipError | null {
    mode.value = 'parlay';

    const samePick = parlayItems.value.findIndex((i) => i.selectionId === item.selectionId);
    if (samePick >= 0) {
      lastParlayError.value = null;
      return null;
    }

    if (parlayItems.value.some((i) => i.matchId === item.matchId)) {
      lastParlayError.value = 'SAME_MATCH';
      return 'SAME_MATCH';
    }

    const check = canSelectForParlay({
      marketType: item.marketType,
      lineValue: parseLineValue(item.lineValue),
      allowParlay: item.allowParlay,
    });
    if (!check.ok) {
      lastParlayError.value = check.reason;
      return check.reason;
    }

    if (parlayItems.value.length >= PARLAY_MAX_LEGS) {
      lastParlayError.value = 'MAX_LEGS';
      return 'MAX_LEGS';
    }

    parlayItems.value.unshift(item);
    lastParlayError.value = null;
    return null;
  }

  function addSingleToParlay(): ParlaySlipError | null {
    if (!singleItem.value) return null;
    return addParlayLeg(singleItem.value);
  }

  function removeItem(selectionId: string) {
    if (singleItem.value?.selectionId === selectionId) {
      singleItem.value = null;
    }
    singleCartItems.value = singleCartItems.value.filter((i) => i.selectionId !== selectionId);
    parlayItems.value = parlayItems.value.filter((i) => i.selectionId !== selectionId);
    clearItemStake(selectionId);
    if (mode.value === 'parlay' && !parlayItems.value.length) mode.value = 'single';
    lastParlayError.value = null;
  }

  function clearSingle() {
    const legacy = singleItem.value;
    for (const item of singleCartItems.value) clearItemStake(item.selectionId);
    if (legacy) clearItemStake(legacy.selectionId);
    singleItem.value = null;
    singleCartItems.value = [];
    if (mode.value === 'single') lastParlayError.value = null;
  }

  function clearParlay() {
    parlayItems.value = [];
    mode.value = 'single';
    lastParlayError.value = null;
  }

  function clear() {
    if (mode.value === 'parlay') {
      clearParlay();
      return;
    }
    clearSingle();
  }

  function clearAll() {
    singleItem.value = null;
    singleCartItems.value = [];
    parlayItems.value = [];
    clearAllItemStakes();
    mode.value = 'single';
    lastParlayError.value = null;
    removePersisted(auth.user?.id ?? null);
  }

  const totalOdds = computed(() =>
    items.value.reduce((acc, i) => acc * i.odds, 1),
  );

  const potentialReturn = computed(() => {
    if (!items.value.length) return 0;
    if (mode.value === 'parlay' && canPlaceParlay.value) {
      return stake.value * totalOdds.value;
    }
    return items.value.reduce(
      (acc, i) => acc + getItemStake(i.selectionId) * i.odds,
      0,
    );
  });

  const totalSingleStake = computed(() =>
    items.value.reduce((acc, i) => acc + getItemStake(i.selectionId), 0),
  );

  const canPlaceParlay = computed(
    () =>
      mode.value === 'parlay' &&
      parlayItems.value.length >= PARLAY_MIN_LEGS &&
      parlayItems.value.length <= PARLAY_MAX_LEGS,
  );

  const canPlaceBatchSingles = computed(
    () => mode.value === 'single' && (singleCartItems.value.length > 0 || singleItem.value != null),
  );

  const canSubmit = computed(
    () => canPlaceParlay.value || canPlaceBatchSingles.value,
  );

  const drawerOpen = ref(false);
  const panelExpanded = ref(readDesktopPanelExpanded());

  watch(panelExpanded, (expanded) => {
    writeDesktopPanelExpanded(expanded);
  });

  function openDrawer() {
    drawerOpen.value = true;
  }

  function closeDrawer() {
    drawerOpen.value = false;
  }

  function expandPanel() {
    panelExpanded.value = true;
  }

  function collapsePanel() {
    panelExpanded.value = false;
  }

  function togglePanelExpanded() {
    panelExpanded.value = !panelExpanded.value;
  }

  function updateSelectionOdds(selectionId: string, odds: number, oddsVersion: string) {
    if (singleItem.value?.selectionId === selectionId) {
      singleItem.value = { ...singleItem.value, odds, oddsVersion };
    }
    const cartIdx = singleCartItems.value.findIndex((i) => i.selectionId === selectionId);
    if (cartIdx >= 0) {
      singleCartItems.value[cartIdx] = { ...singleCartItems.value[cartIdx], odds, oddsVersion };
    }
    const idx = parlayItems.value.findIndex((i) => i.selectionId === selectionId);
    if (idx >= 0) {
      parlayItems.value[idx] = { ...parlayItems.value[idx], odds, oddsVersion };
    }
  }

  return {
    singleItem,
    singleCartItems,
    parlayItems,
    singleStakes,
    items,
    stake,
    mode,
    singleCount,
    parlayCount,
    totalCount,
    count,
    isParlay,
    totalOdds,
    potentialReturn,
    totalSingleStake,
    canPlaceParlay,
    canPlaceBatchSingles,
    canSubmit,
    lastParlayError,
    drawerOpen,
    panelExpanded,
    setMode,
    setSingleItem,
    addItem,
    addToSingleCart,
    isInSlip,
    addParlayLeg,
    addSingleToParlay,
    removeItem,
    clearSingle,
    clearParlay,
    clear,
    clearAll,
    openDrawer,
    closeDrawer,
    expandPanel,
    collapsePanel,
    togglePanelExpanded,
    updateSelectionOdds,
    getItemStake,
    setItemStake,
    initItemStake,
  };
});
