//! Instruction discriminators + payload types (**Ore**: single `instruction.rs`).

use steel::*;

/// One-time **`Initialize`**: creates **`Treasury`** PDA + treasury USDC ATA. Economics and routing pubkeys are [`crate::consts`] (**`FEE_COLLECTOR`** for team-fee SPL legs). **`payer`** must be [`ADMIN_ADDRESS`](crate::consts::ADMIN_ADDRESS).
///
/// **Accounts:** **`payer`**, **`treasury`**, **`mint`**, **`treasury_ata`**, **`system_program`**, **`token_program`**, **`ata_program`**.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Initialize {}

/// Purchase tickets: user pays **`amount`** USDC (**micro base units**); team cut to fee collector; remainder splits into **`Treasury`** pools; **`Ledger`** credits **`unstaked_micros`** and **`tickets`**.
///
/// **`previous_*`** resolves **`series_id`** + **`period - 1`** (same **`series_id`**).
///
/// **Accounts:** **`user`**, **`treasury`**, **`ledger`**, **`mint`**, **`user_ata`**, **`treasury_ata`**, **`team_ata`**, **`system_program`**, **`token_program`**, **`ata_program`**, **`previous_market`**, **`previous_position`**.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct BuyTickets {
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 6],
    pub period: [u8; 8],
    pub amount: [u8; 8],
}

/// Spend **`ticket_units`** from **`Ledger`** into **`Position`** / **`Market`** while **`STATUS_OPEN`** and **`now < close_ts`**:
///
/// - **Pre-open commit** (no open oracle yet **or** **`now < open_ts`**): debits tickets; records **[`Position::STATE_COMMITTED_PREOPEN`]**; bumps **`market.committed_{side}`** only (**`total_*`** unchanged).
/// - **Live window** (**open oracle** + **`open_ts ≤ now < close_ts`**): merges any **[`Position::STATE_COMMITTED_PREOPEN`]** into **`total_*`** first; then debits tickets and adds to **`total_*`** as **[`Position::STATE_PENDING`]** (same as legacy stake).
/// - **Activate-only:** **`ticket_units == 0`** allowed only in the live window on an existing **COMMITTED** position (merges into **`total_*`** / **PENDING** without debiting).
///
/// **`period`** in the ix must match **`market.period`** (**PDA derivation**).
///
/// **Accounts:** **`user`**, **`ledger`**, **`treasury`** (**readonly**), **`market`**, **`position`**, **`previous_market`**, **`previous_position`**, **`system_program`**.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct PlaceBet {
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 6],
    pub period: [u8; 8],
    pub ticket_units: [u8; 8],
    pub side: u8,
    pub _pad: [u8; 7],
}

/// Creates **`Market`** (**executor signer**) **or** records open Pyth snapshot (crank).
///
/// **`Create`**: allowed whenever the **`Market`** PDA is **still empty** (executor may calendar **`open_ts`** / **`close_ts`** ahead of **`now`**; live **`PlaceBet`** that writes **`total_*`** remains **`BettingClosed`** until the open oracle anchor exists and **`open_ts ≤ now`**).
///
/// **Accounts:** **`authority`**, **`market`**, **`treasury`** (**readonly**), **`system_program`**, **`pyth_price_feed`** (**readonly**).
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct InitMarket {
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 6],
    pub period: [u8; 8],
    pub open_ts: [u8; 8],
    pub close_ts: [u8; 8],
    pub pyth_price_feed: [u8; 32],
    pub rules_hash: [u8; 32],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct AdminInstantSettlement {
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 6],
    pub period: [u8; 8],
}

/// **`EXECUTOR_KIND_DISTRIBUTE_MARKET_REWARD`**: **`pool`** and **`amount`** must be **0** (**`series_id`** must match **`market.series_id`**).
///
/// **`EXECUTOR_KIND_JACKPOT_PAY`**: SPL **`transfer_checked`**; **`series_id`** should be **`0`**.
///
/// **`EXECUTOR_KIND_MARKET_LINE_RELEASE`**: **`amount`** = **`period`** LE; **`series_id`** in payload.
///
/// **`EXECUTOR_KIND_MERGE_COMMITTED_POSITION`**: **`pool`** and **`amount`** must be **0**; **`series_id`** must match **`position.series_id`**; merges one **[`Position::STATE_COMMITTED_PREOPEN`]** into **`Market::total_*`**. Accounts: **`executor`**, **`treasury`** (**readonly**), **`market`**, **`position`**.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct ExecutorTreasury {
    pub kind: u8,
    pub pool: u8,
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 4],
    pub amount: [u8; 8],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct ClaimPositionFee {}

/// Void an open market when BTC moves < 0.01% (or for oracle failure).
///
/// **Authority**: `ADMIN_ADDRESS` or `EXECUTOR_ADDRESS` (both accepted).
///
/// **Accounts:** `authority` (signer), `treasury` (readonly), `market`.
///
/// Preconditions: `market.status == STATUS_OPEN`, `committed_up == 0`, `committed_down == 0`
/// (all pre-open committed positions must have been merged before voiding).
/// The instruction may be called before or after `close_ts`.
///
/// After voiding, positions are refunded one-by-one via `AdminRefundVoidPosition`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct AdminVoidMarket {
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 6],
    pub period: [u8; 8],
}

/// Refund a single `STATE_PENDING` position from a voided market back to the player's `Ledger`.
///
/// **Authority**: `EXECUTOR_ADDRESS`.
///
/// **Accounts:** `authority` (signer), `market` (readonly), `position`, `ledger`.
///
/// Preconditions: `market.status == STATUS_VOIDED`, `position.state == STATE_PENDING`.
/// Sets `position.state = STATE_COMMIT_REFUNDED`; restores `stake / TICKET_MICROS` tickets and
/// `stake` `unstaked_micros` to the player's `Ledger`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct AdminRefundVoidPosition {
    pub series_id: [u8; 2],
    pub _pad_ix: [u8; 6],
    pub period: [u8; 8],
}

/// Advance the global week counter (`Treasury::current_week += 1`).
///
/// **Authority**: `EXECUTOR_ADDRESS`.
///
/// **Accounts:** `authority` (signer), `treasury`.
///
/// Call once at the start of each scoring week. All `Ledger` accounts will lazily zero their
/// `week_*` counters the next time any instruction touches them (no sweep required).
/// The new week number is returned in the `current_week` field of `Treasury` after the ix.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct AdminInitWeek {}

pub const EXECUTOR_KIND_DISTRIBUTE_MARKET_REWARD: u8 = 0;
pub const EXECUTOR_KIND_JACKPOT_PAY: u8 = 1;
pub const EXECUTOR_KIND_MARKET_LINE_RELEASE: u8 = 2;
pub const EXECUTOR_KIND_MERGE_COMMITTED_POSITION: u8 = 3;

use num_enum::TryFromPrimitive;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum StreakInstruction {
    Initialize = 0,
    BuyTickets = 1,
    PlaceBet = 2,
    InitMarket = 3,
    AdminInstantSettlement = 4,
    ExecutorTreasury = 5,
    ClaimPositionFee = 6,
    AdminVoidMarket = 7,
    AdminRefundVoidPosition = 8,
    AdminInitWeek = 9,
}

instruction!(StreakInstruction, Initialize);
instruction!(StreakInstruction, BuyTickets);
instruction!(StreakInstruction, PlaceBet);
instruction!(StreakInstruction, InitMarket);
instruction!(StreakInstruction, AdminInstantSettlement);
instruction!(StreakInstruction, ExecutorTreasury);
instruction!(StreakInstruction, ClaimPositionFee);
instruction!(StreakInstruction, AdminVoidMarket);
instruction!(StreakInstruction, AdminRefundVoidPosition);
instruction!(StreakInstruction, AdminInitWeek);
