use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyAiManual {
    pub title: String,
    pub language: String,
    pub overview: Vec<String>,
    pub statement_blocks: Vec<ManualSection>,
    pub runtime_field_groups: Vec<ManualFieldGroup>,
    pub functions: Vec<ManualFunction>,
    pub factor_sources: Vec<ManualFactorSource>,
    pub examples: Vec<ManualExample>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualSection {
    pub title: String,
    pub detail: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualFieldGroup {
    pub title: String,
    pub detail: String,
    pub fields: Vec<ManualField>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualField {
    pub name: String,
    pub field_type: String,
    pub detail: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualFunction {
    pub name: String,
    pub signature: String,
    pub detail: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualFactorSource {
    pub table: String,
    pub detail: String,
    pub fields: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualExample {
    pub title: String,
    pub code: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StrategyAiCatalog {
    pub daily_fields: Vec<String>,
    pub benchmark_fields: Vec<String>,
    pub indicator_factors: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyAiGenerateRequest {
    pub user_goal: String,
    pub constraints: Vec<String>,
    pub market: String,
    pub benchmark_symbol: String,
    pub signal_symbol: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyAiOptimizeRequest {
    pub current_code: String,
    pub objective: String,
    pub result_summary: serde_json::Value,
    pub diagnostics: Vec<String>,
}

pub fn built_in_strategy_manual() -> StrategyAiManual {
    StrategyAiManual {
        title: "OmniQuant 平台策略脚本手册".to_string(),
        language: "engine-script".to_string(),
        overview: vec![
            "平台策略脚本采用声明式 DSL + 表达式执行模型。".to_string(),
            "支持 let 变量、fn 自定义函数、when/unless/else 条件块、数据库字段因子映射。".to_string(),
            "禁止自由 Python/JavaScript 命令式语句，最终必须输出平台 DSL。".to_string(),
        ],
        statement_blocks: vec![
            ManualSection {
                title: "strategy(\"name\") { ... }".to_string(),
                detail: "策略入口。内部用于声明 market/benchmark/signal、选股、调仓、风控、排序、资金分配。name 是策略标识，会进入历史档案和回测记录，建议始终填写，但它本身不改变交易逻辑。".to_string(),
            },
            ManualSection {
                title: "market / benchmark / signal".to_string(),
                detail: "market(\"CN_A\") 指定市场，benchmark 和 signal 使用标准证券代码，例如 000852.SH。".to_string(),
            },
            ManualSection {
                title: "rebalance.every_days(n).at([..])".to_string(),
                detail: "设置调仓周期和盘中决策/执行时刻。".to_string(),
            },
            ManualSection {
                title: "rebalance.weekly / rebalance.monthly".to_string(),
                detail: "支持按交易周或交易月调仓，例如 rebalance.weekly(weekday=5).at([\"10:18\"])、rebalance.weekly(tradingday=-1).at([\"10:18\"])、rebalance.monthly(tradingday=1).at([\"10:18\"])。当前这些调度规则会在 on_day 阶段触发。".to_string(),
            },
            ManualSection {
                title: "selection.market_cap_band / selection.limit / ordering.rank_by / ordering.rank_expr".to_string(),
                detail: "控制候选范围、数量和排序。支持表达式驱动的动态市值带和排序表达式。".to_string(),
            },
            ManualSection {
                title: "filter.stock_expr / risk.stop_loss / risk.take_profit / allocation.buy_scale".to_string(),
                detail: "表达式型规则，支持多条组合。stop_loss/take_profit 多条按 OR 组合，filter.stock_expr 多条按 AND 组合。".to_string(),
            },
            ManualSection {
                title: "corporate_actions.dividend_reinvestment".to_string(),
                detail: "支持 corporate_actions.dividend_reinvestment(true)。开启后，现金分红到账会优先按 round lot 回补成同一只股票，零头保留为现金。".to_string(),
            },
            ManualSection {
                title: "execution.matching_type / execution.slippage".to_string(),
                detail: "设置撮合模式和滑点。支持 execution.matching_type(\"next_tick_last\" | \"next_tick_best_own\" | \"next_tick_best_counterparty\" | \"counterparty_offer\" | \"vwap\" | \"current_bar_close\" | \"next_bar_open\" | \"open_auction\")。其中 next_tick_last 使用 tick 的 last_price；next_tick_best_own / next_tick_best_counterparty 会按 L1 买一卖一近似 rqalpha 的 tick 最优价语义，counterparty_offer 当前也按 L1 对手方报价近似实现；vwap 会在盘中执行价链路上聚合多笔成交为单条 VWAP 成交；open_auction 使用当日集合竞价开盘价 day_open 进行撮合，且不额外施加滑点，并按竞价成交量而不是盘口一档流动性限制成交；滑点支持 execution.slippage(\"none\") / execution.slippage(\"price_ratio\", 0.001) / execution.slippage(\"tick_size\", 1) / execution.slippage(\"limit_price\")，其中 limit_price 会在限价单成交时按挂单价模拟 rqalpha 的最坏成交价。".to_string(),
            },
            ManualSection {
                title: "trading.rotation / order.* / cancel.*".to_string(),
                detail: "支持显式下单和撤单。可以用 trading.rotation(false) 关闭默认轮动链路，再写 order.shares(\"600000.SH\", 1000)、order.value(\"600000.SH\", cash * 0.25)、order.target_percent(\"600000.SH\", 0.05)、order.limit_value(\"600000.SH\", cash * 0.25, open * 0.99)、cancel.order(12345)、cancel.symbol(\"600000.SH\")、cancel.all()。symbol 使用标准证券代码；数量、金额、仓位、限价和 order_id 都支持表达式；这些语句也支持放进 when/unless 条件块。".to_string(),
            },
            ManualSection {
                title: "when / unless / else".to_string(),
                detail: "条件块支持按日期、指数、仓位等动态切换规则。".to_string(),
            },
        ],
        runtime_field_groups: vec![
            ManualFieldGroup {
                title: "日级字段".to_string(),
                detail: "可直接在表达式中使用的日级与账户字段。".to_string(),
                fields: vec![
                    ManualField { name: "signal_open/signal_close".to_string(), field_type: "float".to_string(), detail: "信号指数当日开盘价与前一日收盘价。".to_string() },
                    ManualField { name: "benchmark_open/benchmark_close".to_string(), field_type: "float".to_string(), detail: "基准当日开盘价与前一日收盘价。".to_string() },
                    ManualField { name: "signal_ma5/signal_ma10/signal_ma20/signal_ma30".to_string(), field_type: "float".to_string(), detail: "信号指数滚动均线。".to_string() },
                    ManualField { name: "benchmark_ma5/benchmark_ma10/benchmark_ma20/benchmark_ma30".to_string(), field_type: "float".to_string(), detail: "基准指数滚动均线。".to_string() },
                    ManualField { name: "cash/available_cash/market_value/total_equity".to_string(), field_type: "float".to_string(), detail: "账户资金与总资产。".to_string() },
                    ManualField { name: "position_count/max_positions/refresh_rate".to_string(), field_type: "int".to_string(), detail: "仓位计数与调仓周期。".to_string() },
                    ManualField { name: "year/month/quarter/day_of_month/day_of_year/week_of_year/weekday".to_string(), field_type: "int".to_string(), detail: "日期维度字段。".to_string() },
                    ManualField { name: "is_month_start/is_month_end".to_string(), field_type: "bool".to_string(), detail: "月初/月末标记。".to_string() },
                ],
            },
            ManualFieldGroup {
                title: "个股字段".to_string(),
                detail: "每只候选股票的运行时字段。".to_string(),
                fields: vec![
                    ManualField { name: "symbol".to_string(), field_type: "string".to_string(), detail: "证券代码。".to_string() },
                    ManualField { name: "market_cap/free_float_cap".to_string(), field_type: "float".to_string(), detail: "总市值、流通市值。".to_string() },
                    ManualField { name: "turnover_ratio/effective_turnover_ratio".to_string(), field_type: "float".to_string(), detail: "换手率、有效换手率。".to_string() },
                    ManualField { name: "open/close/last/last_price/prev_close".to_string(), field_type: "float".to_string(), detail: "开收盘与盘中价格。".to_string() },
                    ManualField { name: "upper_limit/lower_limit/price_tick/round_lot/minimum_order_quantity/order_step_size".to_string(), field_type: "float/int".to_string(), detail: "涨跌停、最小价位、整手、最小下单量和数量步长。KSH/BJSE 等板块可与 round_lot 不同。".to_string() },
                    ManualField { name: "paused/is_st/is_kcb/is_one_yuan/is_new_listing".to_string(), field_type: "bool".to_string(), detail: "可交易性与板块标志。".to_string() },
                    ManualField { name: "allow_buy/allow_sell/at_upper_limit/at_lower_limit".to_string(), field_type: "bool".to_string(), detail: "盘中买卖与涨跌停状态。".to_string() },
                    ManualField { name: "touched_upper_limit/touched_lower_limit/hit_upper_limit/hit_lower_limit".to_string(), field_type: "bool".to_string(), detail: "当日 tick 曾经触达涨跌停。".to_string() },
                    ManualField { name: "stock_ma5/stock_ma10/stock_ma20/stock_ma30".to_string(), field_type: "float".to_string(), detail: "个股价格均线内建别名。只内建这几个窗口；15 日、45 日等任意窗口请改用 sma(\"close\", n)。".to_string() },
                    ManualField { name: "stock_volume_ma5/stock_volume_ma10/stock_volume_ma20/stock_volume_ma60".to_string(), field_type: "float".to_string(), detail: "个股成交量均线内建别名。只内建这几个窗口；任意窗口请改用 rolling_mean(\"volume\", n)。".to_string() },
                    ManualField { name: "listed_days".to_string(), field_type: "int".to_string(), detail: "上市天数。".to_string() },
                ],
            },
            ManualFieldGroup {
                title: "持仓字段".to_string(),
                detail: "在持仓止盈止损表达式中可用。".to_string(),
                fields: vec![
                    ManualField { name: "avg_cost".to_string(), field_type: "float".to_string(), detail: "持仓均价。".to_string() },
                    ManualField { name: "current_price".to_string(), field_type: "float".to_string(), detail: "当前盘中价格。".to_string() },
                    ManualField { name: "holding_return".to_string(), field_type: "float".to_string(), detail: "持仓收益率，小数。".to_string() },
                    ManualField { name: "profit_pct".to_string(), field_type: "float".to_string(), detail: "持仓收益率，百分比。".to_string() },
                    ManualField { name: "quantity/sellable_qty".to_string(), field_type: "int".to_string(), detail: "持仓数量与可卖数量。".to_string() },
                ],
            },
        ],
        functions: vec![
            ManualFunction { name: "factor".to_string(), signature: "factor(\"column_name\")".to_string(), detail: "读取当前股票的数据库因子列。".to_string() },
            ManualFunction { name: "day_factor".to_string(), signature: "day_factor(\"field_name\")".to_string(), detail: "读取日级/指数级字段映射。".to_string() },
            ManualFunction { name: "rolling_mean".to_string(), signature: "rolling_mean(\"field\", lookback)".to_string(), detail: "任意字段滚动均值，支持 volume/amount/turnover_ratio、signal_open/signal_close、benchmark_open/benchmark_close 等。任意成交量窗口推荐用它，比如 rolling_mean(\"volume\", 15)。".to_string() },
            ManualFunction { name: "sma".to_string(), signature: "sma(\"field\", lookback)".to_string(), detail: "rolling_mean 的别名。任意价格均线窗口推荐用它，比如 sma(\"close\", 15)。".to_string() },
            ManualFunction { name: "round/floor/ceil/abs/min/max/clamp".to_string(), signature: "round(x)".to_string(), detail: "常用数值函数。".to_string() },
            ManualFunction { name: "safe_div".to_string(), signature: "safe_div(lhs, rhs, fallback)".to_string(), detail: "安全除法。".to_string() },
            ManualFunction { name: "contains/starts_with/ends_with/lower/upper/trim/strlen".to_string(), signature: "starts_with(symbol, \"60\")".to_string(), detail: "字符串辅助函数。".to_string() },
        ],
        factor_sources: vec![
            ManualFactorSource {
                table: "fi_data_center.bt_daily_features_v1".to_string(),
                detail: "回测日频主表，提供价格、均线、换手率、涨跌停触达等字段。".to_string(),
                fields: vec![],
            },
            ManualFactorSource {
                table: "fi_data_center.benchmark_daily_v1".to_string(),
                detail: "指数/基准日线表。".to_string(),
                fields: vec![],
            },
            ManualFactorSource {
                table: "fi_data_center.stock_indicator_factors_v1".to_string(),
                detail: "股票指标因子原表，可映射进 factors[...]。".to_string(),
                fields: vec![],
            },
        ],
        examples: vec![
            ManualExample {
                title: "均线 + 均量过滤".to_string(),
                code: "filter.stock_expr(stock_ma5 > stock_ma20 && rolling_mean(\"volume\", 5) < rolling_mean(\"volume\", 60))".to_string(),
            },
            ManualExample {
                title: "涨停触达后满仓，否则半仓".to_string(),
                code: "allocation.buy_scale(touched_upper_limit ? 1.0 : 0.5)".to_string(),
            },
            ManualExample {
                title: "next tick 撮合 + tick 滑点".to_string(),
                code: "execution.matching_type(\"next_tick_last\")\nexecution.slippage(\"tick_size\", 1)".to_string(),
            },
            ManualExample {
                title: "显式下单并关闭默认轮动".to_string(),
                code: "trading.rotation(false)\norder.value(\"600000.SH\", cash * 0.25, \"manual_entry\")\ncancel.symbol(\"600000.SH\", \"manual_cancel\")".to_string(),
            },
        ],
    }
}

pub fn merge_catalog_into_manual(
    mut manual: StrategyAiManual,
    catalog: &StrategyAiCatalog,
) -> StrategyAiManual {
    for source in &mut manual.factor_sources {
        if source.table.ends_with("bt_daily_features_v1") {
            source.fields = catalog.daily_fields.clone();
        } else if source.table.ends_with("benchmark_daily_v1") {
            source.fields = catalog.benchmark_fields.clone();
        } else if source.table.ends_with("stock_indicator_factors_v1") {
            source.fields = catalog.indicator_factors.clone();
        }
    }
    manual
}

pub fn render_manual_markdown(manual: &StrategyAiManual) -> String {
    let mut out = String::new();
    out.push_str(&format!("# {}\n\n", manual.title));
    out.push_str(&format!("语言: `{}`\n\n", manual.language));
    if !manual.overview.is_empty() {
        out.push_str("## 概览\n");
        for item in &manual.overview {
            out.push_str(&format!("- {}\n", item));
        }
        out.push('\n');
    }
    out.push_str("## 语句块\n");
    for item in &manual.statement_blocks {
        out.push_str(&format!("- `{}`: {}\n", item.title, item.detail));
    }
    out.push('\n');
    out.push_str("## 运行时字段\n");
    for group in &manual.runtime_field_groups {
        out.push_str(&format!("### {}\n{}\n", group.title, group.detail));
        for field in &group.fields {
            out.push_str(&format!(
                "- `{}` (`{}`): {}\n",
                field.name, field.field_type, field.detail
            ));
        }
        out.push('\n');
    }
    out.push_str("## 内建函数\n");
    for function in &manual.functions {
        out.push_str(&format!(
            "- `{}` / `{}`: {}\n",
            function.name, function.signature, function.detail
        ));
    }
    out.push('\n');
    out.push_str("## 数据库字段与因子\n");
    for source in &manual.factor_sources {
        out.push_str(&format!("### `{}`\n{}\n", source.table, source.detail));
        if source.fields.is_empty() {
            out.push_str("- 当前未加载字段清单\n");
        } else {
            for field in &source.fields {
                out.push_str(&format!("- `{}`\n", field));
            }
        }
        out.push('\n');
    }
    out.push_str("## 示例\n");
    for example in &manual.examples {
        out.push_str(&format!(
            "### {}\n```txt\n{}\n```\n\n",
            example.title, example.code
        ));
    }
    out
}

pub fn build_generation_prompt(
    manual_markdown: &str,
    request: &StrategyAiGenerateRequest,
) -> String {
    let mut prompt = String::new();
    prompt.push_str("你是 OmniQuant 平台策略脚本生成器。必须输出可运行的平台策略脚本，不要输出 Python 或聚宽语法。\n");
    prompt.push_str("必须遵守以下约束：\n");
    prompt.push_str("- 只输出平台策略代码。\n");
    prompt.push_str("- 不要输出解释文本。\n");
    prompt.push_str("- 必须使用 strategy(\"...\") { ... } 语法。\n");
    prompt.push_str("- 如需自定义参数，使用 let 和 fn。\n");
    prompt.push_str("- 优先使用数据库已存在字段和 factors[...]。\n\n");
    prompt.push_str("用户目标：\n");
    prompt.push_str(&format!("- {}\n", request.user_goal));
    if !request.constraints.is_empty() {
        prompt.push_str("约束：\n");
        for item in &request.constraints {
            prompt.push_str(&format!("- {}\n", item));
        }
    }
    prompt.push_str(&format!(
        "\n固定运行条件：market={}, benchmark={}, signal={}\n\n",
        request.market, request.benchmark_symbol, request.signal_symbol
    ));
    prompt.push_str("详细手册如下：\n\n");
    prompt.push_str(manual_markdown);
    prompt
}

pub fn build_optimization_prompt(
    manual_markdown: &str,
    request: &StrategyAiOptimizeRequest,
) -> String {
    let mut prompt = String::new();
    prompt.push_str("你是 OmniQuant 平台策略脚本优化器。必须输出完整、可运行的平台策略脚本，不要输出解释文本。\n");
    prompt.push_str("优化目标：\n");
    prompt.push_str(&format!("- {}\n\n", request.objective));
    prompt.push_str("当前策略代码：\n```txt\n");
    prompt.push_str(&request.current_code);
    prompt.push_str("\n```\n\n");
    if !request.diagnostics.is_empty() {
        prompt.push_str("运行诊断：\n");
        for item in &request.diagnostics {
            prompt.push_str(&format!("- {}\n", item));
        }
        prompt.push('\n');
    }
    prompt.push_str("结果摘要 JSON：\n```json\n");
    prompt.push_str(
        &serde_json::to_string_pretty(&request.result_summary).unwrap_or_else(|_| "{}".to_string()),
    );
    prompt.push_str("\n```\n\n");
    prompt.push_str("详细手册如下：\n\n");
    prompt.push_str(manual_markdown);
    prompt
}
