use chrono::{Datelike, NaiveDate, Utc};
use sea_orm::{
    ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseBackend, EntityTrait, PaginatorTrait,
    QueryFilter, Set, Statement, TransactionTrait,
};
use uuid::Uuid;

use crate::dto::{CreateNumberingRuleReq, GenerateNumberResp, NumberingRuleResp};
use crate::entity::numbering_rule;
use crate::error::{ConfigError, ConfigResult};
use erp_core::audit::AuditLog;
use erp_core::audit_service;
use erp_core::error::check_version;
use erp_core::events::EventBus;
use erp_core::types::Pagination;

/// 格式化编号字符串。
///
/// 拼接规则：
/// 1. 以 `prefix` 开头
/// 2. 若 `prefix` 非空，追加 `separator`
/// 3. 若 `date_part` 为 `Some` 且非空，追加 `date_part` + `separator`
/// 4. 追加零填充的 `seq_current`（填充到 `seq_length` 位，最少 1 位）
pub(crate) fn format_number(
    prefix: &str,
    separator: &str,
    date_part: Option<&str>,
    seq_current: i64,
    seq_length: i32,
) -> String {
    let mut result = String::with_capacity(32);
    result.push_str(prefix);

    if !prefix.is_empty() {
        result.push_str(separator);
    }

    if let Some(dp) = date_part
        && !dp.is_empty()
    {
        result.push_str(dp);
        result.push_str(separator);
    }

    let width = (seq_length.max(1)) as usize;
    let seq_padded = format!("{:0>width$}", seq_current, width = width);
    result.push_str(&seq_padded);

    result
}

/// 编号规则 CRUD 服务 -- 创建、查询、更新、软删除编号规则，
/// 以及线程安全地生成编号序列。
pub struct NumberingService;

impl NumberingService {
    /// 分页查询编号规则列表。
    pub async fn list(
        tenant_id: Uuid,
        pagination: &Pagination,
        db: &sea_orm::DatabaseConnection,
    ) -> ConfigResult<(Vec<NumberingRuleResp>, u64)> {
        let paginator = numbering_rule::Entity::find()
            .filter(numbering_rule::Column::TenantId.eq(tenant_id))
            .filter(numbering_rule::Column::DeletedAt.is_null())
            .paginate(db, pagination.limit());

        let total = paginator
            .num_items()
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;

        let page_index = pagination.page.unwrap_or(1).saturating_sub(1);
        let models = paginator
            .fetch_page(page_index)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;

        let resps: Vec<NumberingRuleResp> = models.iter().map(Self::model_to_resp).collect();

        Ok((resps, total))
    }

    /// 创建编号规则。
    ///
    /// 检查 code 在租户内唯一后插入。
    pub async fn create(
        tenant_id: Uuid,
        operator_id: Uuid,
        req: &CreateNumberingRuleReq,
        db: &sea_orm::DatabaseConnection,
        event_bus: &EventBus,
    ) -> ConfigResult<NumberingRuleResp> {
        // 检查 code 唯一性
        let existing = numbering_rule::Entity::find()
            .filter(numbering_rule::Column::TenantId.eq(tenant_id))
            .filter(numbering_rule::Column::Code.eq(&req.code))
            .filter(numbering_rule::Column::DeletedAt.is_null())
            .one(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;
        if existing.is_some() {
            return Err(ConfigError::DuplicateKey(format!(
                "编号规则编码已存在: {}",
                req.code
            )));
        }

        let now = Utc::now();
        let id = Uuid::now_v7();
        let seq_start = req.seq_start.unwrap_or(1);

        let model = numbering_rule::ActiveModel {
            id: Set(id),
            tenant_id: Set(tenant_id),
            name: Set(req.name.clone()),
            code: Set(req.code.clone()),
            prefix: Set(req.prefix.clone().unwrap_or_default()),
            date_format: Set(req.date_format.clone()),
            seq_length: Set(req.seq_length.unwrap_or(4)),
            seq_start: Set(seq_start),
            seq_current: Set(seq_start as i64),
            separator: Set(req.separator.clone().unwrap_or_else(|| "-".to_string())),
            reset_cycle: Set(req
                .reset_cycle
                .clone()
                .unwrap_or_else(|| "never".to_string())),
            last_reset_date: Set(Some(Utc::now().date_naive())),
            created_at: Set(now),
            updated_at: Set(now),
            created_by: Set(operator_id),
            updated_by: Set(operator_id),
            deleted_at: Set(None),
            version: Set(1),
        };
        model
            .insert(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;

        event_bus
            .publish(
                erp_core::events::DomainEvent::new(
                    "numbering_rule.created",
                    tenant_id,
                    serde_json::json!({ "rule_id": id, "code": req.code }),
                ),
                db,
            )
            .await;

        audit_service::record(
            AuditLog::new(
                tenant_id,
                Some(operator_id),
                "numbering_rule.create",
                "numbering_rule",
            )
            .with_resource_id(id),
            db,
        )
        .await;

        Ok(NumberingRuleResp {
            id,
            name: req.name.clone(),
            code: req.code.clone(),
            prefix: req.prefix.clone().unwrap_or_default(),
            date_format: req.date_format.clone(),
            seq_length: req.seq_length.unwrap_or(4),
            seq_start,
            seq_current: seq_start as i64,
            separator: req.separator.clone().unwrap_or_else(|| "-".to_string()),
            reset_cycle: req
                .reset_cycle
                .clone()
                .unwrap_or_else(|| "never".to_string()),
            last_reset_date: Some(Utc::now().date_naive().to_string()),
            version: 1,
        })
    }

    /// 更新编号规则的可编辑字段。使用乐观锁校验版本。
    pub async fn update(
        id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        req: &crate::dto::UpdateNumberingRuleReq,
        db: &sea_orm::DatabaseConnection,
    ) -> ConfigResult<NumberingRuleResp> {
        let model = numbering_rule::Entity::find_by_id(id)
            .one(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?
            .filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
            .ok_or_else(|| ConfigError::NotFound(format!("编号规则不存在: {id}")))?;

        let next_version =
            check_version(req.version, model.version).map_err(|_| ConfigError::VersionMismatch)?;

        let mut active: numbering_rule::ActiveModel = model.into();

        if let Some(name) = &req.name {
            active.name = Set(name.clone());
        }
        if let Some(prefix) = &req.prefix {
            active.prefix = Set(prefix.clone());
        }
        if let Some(date_format) = &req.date_format {
            active.date_format = Set(Some(date_format.clone()));
        }
        if let Some(seq_length) = req.seq_length {
            active.seq_length = Set(seq_length);
        }
        if let Some(separator) = &req.separator {
            active.separator = Set(separator.clone());
        }
        if let Some(reset_cycle) = &req.reset_cycle {
            active.reset_cycle = Set(reset_cycle.clone());
        }

        active.updated_at = Set(Utc::now());
        active.updated_by = Set(operator_id);
        active.version = Set(next_version);

        let updated = active
            .update(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;

        audit_service::record(
            AuditLog::new(
                tenant_id,
                Some(operator_id),
                "numbering_rule.update",
                "numbering_rule",
            )
            .with_resource_id(id),
            db,
        )
        .await;

        Ok(Self::model_to_resp(&updated))
    }

    /// 软删除编号规则。使用乐观锁校验版本。
    pub async fn delete(
        id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        version: i32,
        db: &sea_orm::DatabaseConnection,
        event_bus: &EventBus,
    ) -> ConfigResult<()> {
        let model = numbering_rule::Entity::find_by_id(id)
            .one(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?
            .filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
            .ok_or_else(|| ConfigError::NotFound(format!("编号规则不存在: {id}")))?;

        let next_version =
            check_version(version, model.version).map_err(|_| ConfigError::VersionMismatch)?;

        let mut active: numbering_rule::ActiveModel = model.into();
        active.deleted_at = Set(Some(Utc::now()));
        active.updated_at = Set(Utc::now());
        active.updated_by = Set(operator_id);
        active.version = Set(next_version);
        active
            .update(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;

        event_bus
            .publish(
                erp_core::events::DomainEvent::new(
                    "numbering_rule.deleted",
                    tenant_id,
                    serde_json::json!({ "rule_id": id }),
                ),
                db,
            )
            .await;

        audit_service::record(
            AuditLog::new(
                tenant_id,
                Some(operator_id),
                "numbering_rule.delete",
                "numbering_rule",
            )
            .with_resource_id(id),
            db,
        )
        .await;

        Ok(())
    }

    /// 线程安全地生成编号。
    ///
    /// 使用 PostgreSQL advisory lock 保证并发安全：
    /// 1. 在事务内获取 pg_advisory_xact_lock
    /// 2. 在同一事务内读取规则、检查重置周期、递增序列、更新数据库
    /// 3. 拼接编号字符串返回
    pub async fn generate_number(
        rule_id: Uuid,
        tenant_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> ConfigResult<GenerateNumberResp> {
        // 先读取规则获取 code（用于 advisory lock）
        let rule = numbering_rule::Entity::find_by_id(rule_id)
            .one(db)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?
            .filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
            .ok_or_else(|| ConfigError::NotFound(format!("编号规则不存在: {rule_id}")))?;

        let rule_code = rule.code.clone();
        let tenant_id_str = tenant_id.to_string();

        // 在同一个事务内获取 advisory lock 并执行编号生成
        // pg_advisory_xact_lock 是事务级别的，锁会在事务结束时自动释放
        let number = db
            .transaction(|txn| {
                let rule_code = rule_code.clone();
                let tenant_id_str = tenant_id_str.clone();
                Box::pin(async move {
                    // 在事务内获取 advisory lock
                    txn.execute(Statement::from_sql_and_values(
                        DatabaseBackend::Postgres,
                        "SELECT pg_advisory_xact_lock(abs(hashtext($1)), abs(hashtext($2))::int)",
                        [rule_code.into(), tenant_id_str.into()],
                    ))
                    .await
                    .map_err(|e| ConfigError::Validation(format!("获取编号锁失败: {e}")))?;

                    // 在同一个事务内执行编号生成
                    Self::generate_number_in_txn(rule_id, tenant_id, txn).await
                })
            })
            .await?;

        Ok(GenerateNumberResp { number })
    }

    /// 事务内执行编号生成逻辑。
    ///
    /// 检查重置周期，必要时重置序列，然后递增并拼接编号。
    async fn generate_number_in_txn<C>(
        rule_id: Uuid,
        tenant_id: Uuid,
        txn: &C,
    ) -> ConfigResult<String>
    where
        C: ConnectionTrait,
    {
        let rule = numbering_rule::Entity::find_by_id(rule_id)
            .one(txn)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?
            .filter(|r| r.tenant_id == tenant_id && r.deleted_at.is_none())
            .ok_or_else(|| ConfigError::NotFound(format!("编号规则不存在: {rule_id}")))?;

        let today = Utc::now().date_naive();
        let mut seq_current = rule.seq_current;

        // 检查是否需要重置序列
        seq_current = Self::maybe_reset_sequence(
            seq_current,
            rule.seq_start as i64,
            &rule.reset_cycle,
            rule.last_reset_date,
            today,
        );

        // 递增序列
        let next_seq = seq_current + 1;

        // 检查序列是否超出 seq_length 能表示的最大值
        let max_val = 10i64.pow(rule.seq_length as u32) - 1;
        if next_seq > max_val {
            return Err(ConfigError::NumberingExhausted(format!(
                "编号序列已耗尽，当前序列号 {next_seq} 超出长度 {} 的最大值",
                rule.seq_length
            )));
        }

        // 更新数据库中的 seq_current 和 last_reset_date
        let mut active: numbering_rule::ActiveModel = rule.clone().into();
        active.seq_current = Set(next_seq);
        active.last_reset_date = Set(Some(today));
        active.updated_at = Set(Utc::now());
        active.version = Set(active.version.take().unwrap_or(0) + 1);
        active
            .update(txn)
            .await
            .map_err(|e| ConfigError::Validation(e.to_string()))?;

        // 拼接编号字符串: {prefix}{separator}{date_part}{separator}{seq_padded}
        let date_part = rule
            .date_format
            .as_ref()
            .map(|fmt| Utc::now().format(fmt).to_string());

        let number = format_number(
            &rule.prefix,
            &rule.separator,
            date_part.as_deref(),
            seq_current,
            rule.seq_length,
        );

        Ok(number)
    }

    /// 根据重置周期判断是否需要重置序列号。
    ///
    /// 如果需要重置，返回 `seq_start`；否则返回原值。
    fn maybe_reset_sequence(
        seq_current: i64,
        seq_start: i64,
        reset_cycle: &str,
        last_reset_date: Option<NaiveDate>,
        today: NaiveDate,
    ) -> i64 {
        let last_reset = match last_reset_date {
            Some(d) => d,
            None => return seq_start, // 从未重置过，使用 seq_start
        };

        match reset_cycle {
            "daily" => {
                if last_reset != today {
                    seq_start
                } else {
                    seq_current
                }
            }
            "monthly" => {
                if last_reset.month() != today.month() || last_reset.year() != today.year() {
                    seq_start
                } else {
                    seq_current
                }
            }
            "yearly" => {
                if last_reset.year() != today.year() {
                    seq_start
                } else {
                    seq_current
                }
            }
            _ => seq_current, // "never" 或其他值不重置
        }
    }

    /// 将数据库模型转换为响应 DTO。
    fn model_to_resp(m: &numbering_rule::Model) -> NumberingRuleResp {
        NumberingRuleResp {
            id: m.id,
            name: m.name.clone(),
            code: m.code.clone(),
            prefix: m.prefix.clone(),
            date_format: m.date_format.clone(),
            seq_length: m.seq_length,
            seq_start: m.seq_start,
            seq_current: m.seq_current,
            separator: m.separator.clone(),
            reset_cycle: m.reset_cycle.clone(),
            last_reset_date: m.last_reset_date.map(|d| d.to_string()),
            version: m.version,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::NaiveDate;

    /// 辅助：构造 NaiveDate
    fn date(y: i32, m: u32, d: u32) -> NaiveDate {
        NaiveDate::from_ymd_opt(y, m, d).unwrap()
    }

    // ---- maybe_reset_sequence 测试 ----

    #[test]
    fn reset_never_keeps_current() {
        // "never" 周期：永远不重置，保持 seq_current
        let result = NumberingService::maybe_reset_sequence(
            100,
            1,
            "never",
            Some(date(2025, 1, 1)),
            date(2026, 4, 15),
        );
        assert_eq!(result, 100);
    }

    #[test]
    fn reset_unknown_cycle_keeps_current() {
        // 未知周期值等同于不重置
        let result = NumberingService::maybe_reset_sequence(
            50,
            1,
            "weekly",
            Some(date(2025, 1, 1)),
            date(2026, 4, 15),
        );
        assert_eq!(result, 50);
    }

    #[test]
    fn reset_daily_same_day_keeps_current() {
        // 同一天内不重置
        let today = date(2026, 4, 15);
        let result = NumberingService::maybe_reset_sequence(42, 1, "daily", Some(today), today);
        assert_eq!(result, 42);
    }

    #[test]
    fn reset_daily_different_day_resets() {
        // 不同天重置为 seq_start
        let result = NumberingService::maybe_reset_sequence(
            42,
            1,
            "daily",
            Some(date(2026, 4, 14)),
            date(2026, 4, 15),
        );
        assert_eq!(result, 1);
    }

    #[test]
    fn reset_daily_resets_with_custom_start() {
        // 重置时使用自定义 seq_start
        let result = NumberingService::maybe_reset_sequence(
            99,
            10,
            "daily",
            Some(date(2026, 4, 10)),
            date(2026, 4, 15),
        );
        assert_eq!(result, 10);
    }

    #[test]
    fn reset_monthly_same_month_keeps_current() {
        // 同月不重置
        let result = NumberingService::maybe_reset_sequence(
            30,
            1,
            "monthly",
            Some(date(2026, 4, 1)),
            date(2026, 4, 15),
        );
        assert_eq!(result, 30);
    }

    #[test]
    fn reset_monthly_different_month_resets() {
        // 不同月份重置
        let result = NumberingService::maybe_reset_sequence(
            30,
            1,
            "monthly",
            Some(date(2026, 3, 31)),
            date(2026, 4, 1),
        );
        assert_eq!(result, 1);
    }

    #[test]
    fn reset_monthly_same_month_different_year_resets() {
        // 不同年份但相同月份数字，仍然重置
        let result = NumberingService::maybe_reset_sequence(
            20,
            5,
            "monthly",
            Some(date(2025, 4, 15)),
            date(2026, 4, 15),
        );
        assert_eq!(result, 5);
    }

    #[test]
    fn reset_yearly_same_year_keeps_current() {
        // 同年不重置
        let result = NumberingService::maybe_reset_sequence(
            50,
            1,
            "yearly",
            Some(date(2026, 1, 1)),
            date(2026, 12, 31),
        );
        assert_eq!(result, 50);
    }

    #[test]
    fn reset_yearly_different_year_resets() {
        // 不同年份重置
        let result = NumberingService::maybe_reset_sequence(
            50,
            1,
            "yearly",
            Some(date(2025, 12, 31)),
            date(2026, 1, 1),
        );
        assert_eq!(result, 1);
    }

    #[test]
    fn reset_no_last_reset_date_returns_seq_start() {
        // 从未重置过，使用 seq_start
        let result =
            NumberingService::maybe_reset_sequence(999, 1, "daily", None, date(2026, 4, 15));
        assert_eq!(result, 1);
    }

    #[test]
    fn reset_no_last_reset_date_uses_custom_start() {
        // 从未重置过，使用自定义 seq_start
        let result =
            NumberingService::maybe_reset_sequence(999, 42, "monthly", None, date(2026, 4, 15));
        assert_eq!(result, 42);
    }

    // ---- model_to_resp 测试 ----

    #[test]
    fn model_to_resp_maps_fields_correctly() {
        let id = Uuid::now_v7();
        let tenant_id = Uuid::now_v7();
        let now = Utc::now();
        let today = now.date_naive();

        let model = numbering_rule::Model {
            id,
            tenant_id,
            name: "订单编号".to_string(),
            code: "ORDER".to_string(),
            prefix: "ORD".to_string(),
            date_format: Some("%Y%m%d".to_string()),
            seq_length: 6,
            seq_start: 1,
            seq_current: 42,
            separator: "-".to_string(),
            reset_cycle: "daily".to_string(),
            last_reset_date: Some(today),
            created_at: now,
            updated_at: now,
            created_by: tenant_id,
            updated_by: tenant_id,
            deleted_at: None,
            version: 3,
        };

        let resp = NumberingService::model_to_resp(&model);

        assert_eq!(resp.id, id);
        assert_eq!(resp.name, "订单编号");
        assert_eq!(resp.code, "ORDER");
        assert_eq!(resp.prefix, "ORD");
        assert_eq!(resp.date_format, Some("%Y%m%d".to_string()));
        assert_eq!(resp.seq_length, 6);
        assert_eq!(resp.seq_start, 1);
        assert_eq!(resp.seq_current, 42);
        assert_eq!(resp.separator, "-");
        assert_eq!(resp.reset_cycle, "daily");
        assert_eq!(resp.last_reset_date, Some(today.to_string()));
        assert_eq!(resp.version, 3);
    }

    #[test]
    fn model_to_resp_none_fields() {
        let id = Uuid::now_v7();
        let tenant_id = Uuid::now_v7();
        let now = Utc::now();

        let model = numbering_rule::Model {
            id,
            tenant_id,
            name: "简单编号".to_string(),
            code: "SIMPLE".to_string(),
            prefix: "".to_string(),
            date_format: None,
            seq_length: 4,
            seq_start: 1,
            seq_current: 1,
            separator: "-".to_string(),
            reset_cycle: "never".to_string(),
            last_reset_date: None,
            created_at: now,
            updated_at: now,
            created_by: tenant_id,
            updated_by: tenant_id,
            deleted_at: None,
            version: 1,
        };

        let resp = NumberingService::model_to_resp(&model);

        assert_eq!(resp.date_format, None);
        assert_eq!(resp.last_reset_date, None);
        assert_eq!(resp.prefix, "");
    }

    // ---- format_number 测试 ----

    #[test]
    fn format_basic_prefix_no_date() {
        // 基础：前缀 + 序列号
        let result = format_number("ORD", "/", None, 1, 5);
        assert_eq!(result, "ORD/00001");
    }

    #[test]
    fn format_with_date_part() {
        // 前缀 + 日期 + 序列号
        let result = format_number("INV", "-", Some("20260430"), 42, 4);
        assert_eq!(result, "INV-20260430-0042");
    }

    #[test]
    fn format_no_prefix() {
        // 无前缀，直接输出序列号
        let result = format_number("", "/", None, 7, 3);
        assert_eq!(result, "007");
    }

    #[test]
    fn format_no_prefix_no_date() {
        // 无前缀无日期，仅序列号
        let result = format_number("", "-", None, 99, 6);
        assert_eq!(result, "000099");
    }

    #[test]
    fn format_seq_length_zero_pads_to_one() {
        // seq_length=0 时仍至少填充 1 位
        let result = format_number("", "", None, 5, 0);
        assert_eq!(result, "5");
    }
}
