use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use sqlx::FromRow;
use std::convert::TryFrom;

// 定义JWT载荷结构体
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
    // 标准字段：过期时间（必须）
    pub exp: i64, // 时间戳（秒）
    // 标准字段：签发时间
    pub iat: i64,
    // 自定义字段：用户ID（根据业务需求添加）
    pub user_id: i32,
    // 自定义字段：openid（可选）
    pub openid: String,
    // 自定义字段：user_ytpe
    pub user_type: i32,
}

// 临时访问用 token
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TempTokenClaims {
    pub exp: i64,         // 过期时间（时间戳）
    pub iat: i64,         // 签发时间
    pub openid: String,   // 用户身份标识
    pub resource_id: i32, // 允许访问的资源ID（如天气数据id）
}

// 登录成功后的令牌响应结构体（双 token）
#[derive(Debug, Serialize, Clone)]
pub struct LoginResponse {
    pub success: bool,
    pub token: String,
    pub refresh_token: String,
}

impl LoginResponse {
    pub fn new(token: String, refresh_token: String) -> Self {
        Self {
            success: true,
            token,
            refresh_token,
        }
    }
}

// 兼容旧的 TokenResponse
#[derive(Debug, Serialize, Clone)]
pub struct TokenResponse {
    pub success: bool,
    pub token: String,
}

impl TokenResponse {
    pub fn new(token: String) -> Self {
        Self {
            success: true,
            token,
        }
    }
}

// Refresh Token 请求
#[derive(Debug, Deserialize)]
pub struct RefreshTokenRequest {
    pub refresh_token: String,
}

// Token 刷新响应
#[derive(Debug, Serialize)]
pub struct TokenRefreshResponse {
    pub success: bool,
    pub token: String,
    pub refresh_token: String,
}

#[derive(Debug, Deserialize)]
pub struct WeChatLoginRequest {
    pub code: String,
}

#[derive(Debug, Deserialize)]
pub struct WeChatApiResponse {
    pub openid: Option<String>,
    pub errcode: Option<i32>,
    pub errmsg: Option<String>,
}

// 定义新类型，包装 Vec<f64>（当前 crate 内的类型）
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FloatVec(pub Vec<f64>);

// 为 Vec<f64> 实现从 JsonValue 的转换
impl TryFrom<JsonValue> for FloatVec {
    type Error = String;

    fn try_from(value: JsonValue) -> Result<Self, Self::Error> {
        // 检查 JSON 值是否为数组
        let arr = value
            .as_array()
            .ok_or_else(|| "JSON 类型错误：期望数组，但得到其他类型".to_string())?;

        // 遍历数组元素，转换为 f64
        let mut result = Vec::with_capacity(arr.len());
        for item in arr {
            let num = item
                .as_f64()
                .ok_or_else(|| format!("JSON 元素类型错误：期望数字，但得到 {:?}", item))?;
            result.push(num);
        }

        Ok(FloatVec(result))
    }
}

#[derive(Debug, Deserialize, Serialize, FromRow)] // 添加 FromRow 派生
pub struct WeatherDataBrief {
    #[sqlx(rename = "id")] // 数据库表的 id 列（自增主键），查询映射时使用
    pub id: i32,

    pub title: String,

    pub date: NaiveDate,

    #[serde(rename = "hours")]
    #[sqlx(rename = "hour")] // 数据库中是 hour 字段
    pub hours: i32,

    #[serde(rename = "min")]
    #[sqlx(rename = "min")]
    pub min: i32,

    pub longitude: String,
    pub latitude: String,
    #[serde(rename = "isFavorite", default)]
    pub is_favorite: bool,
}

// 在models.rs中添加（可放在WeatherDataBrief下方）
#[derive(Debug, Serialize, FromRow)]
pub struct WeatherListResponse {
    pub list: Vec<WeatherDataBrief>, // 分页数据列表
    pub total: i64,                  // 总数据条数
}

// 天气数据结构体（修改后）
#[derive(Debug, Deserialize, Serialize, FromRow)]
pub struct WeatherData {
    #[serde(skip_deserializing)]
    #[sqlx(rename = "id")]
    pub id: i32,

    #[serde(rename = "areaType")]
    #[sqlx(rename = "areatype")]
    pub area_type: String,

    #[serde(rename = "assignmentNumber")]
    #[sqlx(rename = "assignmentnumber")]
    pub assignment_number: Option<String>,

    #[serde(rename = "atmosphericStability")]
    #[sqlx(rename = "atmosphericstability")]
    pub atmospheric_stability: String,

    #[serde(rename = "averageWindDirection")]
    #[sqlx(rename = "averagewinddirection")]
    pub average_wind_direction: f64,

    #[serde(rename = "averageWindSpeed")]
    #[sqlx(rename = "averagewindspeed")]
    pub average_wind_speed: f64,

    #[serde(
        rename = "calculatedWindSpeed",
        alias = "calculated_wind_speed",
        alias = "calculatedwindspeed"
    )]
    #[sqlx(rename = "calculatedwindspeed")]
    pub calculated_wind_speed: Option<f64>, // 这个已经是 Option，很好

    #[serde(rename = "cloudIndex")]
    #[sqlx(rename = "cloudindex")]
    pub cloud_index: String,

    // --- 以下是修改的重点 ---
    // 这些字段在前端是条件性发送的，因此在 Rust 中也必须是可选的
    #[serde(rename = "convertedWindSpeed")]
    #[sqlx(rename = "convertedwindspeed")]
    pub converted_wind_speed: Option<f64>,

    pub date: NaiveDate,

    #[serde(rename = "daySinceJanFirst")]
    #[sqlx(rename = "daysincejanfirst")]
    pub day_since_jan_first: i32,

    #[serde(rename = "hasMeasuredWindSpeed")]
    #[sqlx(rename = "hasmeasuredwindspeed")]
    pub has_measured_wind_speed: bool,

    #[serde(rename = "hasSpotCheckWindSpeed")]
    #[sqlx(rename = "hasspotcheckwindspeed")]
    pub has_spot_check_wind_speed: bool,

    #[serde(rename = "hours")]
    #[sqlx(rename = "hour")]
    pub hours: i32,

    #[serde(rename = "inspectionType")]
    #[sqlx(rename = "inspectiontype")]
    pub inspection_type: Option<String>,

    pub latitude: String,
    pub longitude: String,

    #[serde(rename = "lowCloudiness")]
    #[sqlx(rename = "lowcloudiness")]
    pub low_cloudiness: String,

    #[serde(rename = "measuredWindSpeed")]
    #[sqlx(rename = "measuredwindspeed")]
    pub measured_wind_speed: Option<f64>,

    #[serde(rename = "pointHeight")]
    #[sqlx(rename = "pointheight")]
    pub point_height: Option<f64>,

    #[serde(rename = "min")]
    #[sqlx(rename = "min")]
    pub min: i32,

    pub openid: String,

    #[serde(rename = "overallCloudiness")]
    #[sqlx(rename = "overallcloudiness")]
    pub overall_cloudiness: String,

    #[serde(rename = "overallSuitability")]
    #[sqlx(rename = "overallsuitability")]
    pub overall_suitability: String,

    #[serde(rename = "pointWindSpeed")]
    #[sqlx(rename = "pointwindspeed")]
    pub point_wind_speed: Option<f64>, // <-- 修改: f64 -> Option<f64>

    #[serde(rename = "solarDeclination")]
    #[sqlx(rename = "solardeclination")]
    pub solar_declination: f64,

    #[serde(rename = "solarRadiationLevel")]
    #[sqlx(rename = "solarradiationlevel")]
    pub solar_radiation_level: i32,

    #[serde(rename = "suitabilityDegree")]
    #[sqlx(rename = "suitabilitydegree")]
    pub suitability_degree: String,

    #[serde(rename = "sunAltitude")]
    #[sqlx(rename = "sunaltitude")]
    pub sun_altitude: f64,

    pub theta: f64,
    pub title: String,

    pub version: String,

    #[serde(rename = "windDirection")]
    #[sqlx(rename = "winddirection")]
    #[sqlx(try_from = "serde_json::Value")]
    pub wind_direction: FloatVec,

    #[serde(rename = "windDirectionStandardDeviation")]
    #[sqlx(rename = "winddirectionstandarddeviation")]
    pub wind_direction_standard_deviation: f64,

    #[serde(rename = "windDirectionSuitability")]
    #[sqlx(rename = "winddirectionsuitability")]
    pub wind_direction_suitability: String,

    #[serde(rename = "windSpeed")]
    #[sqlx(rename = "windspeed")]
    #[sqlx(try_from = "serde_json::Value")]
    pub wind_speed: FloatVec,

    #[serde(rename = "windSpeedSuitability")]
    #[sqlx(rename = "windspeedsuitability")]
    pub wind_speed_suitability: String,

    #[serde(skip_deserializing)]
    #[serde(rename = "isFavorite", default)]
    pub is_favorite: bool,
}

#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct User {
    pub id: i32,
    #[serde(rename = "name")]
    #[sqlx(rename = "name")]
    pub name: String,
    #[sqlx(rename = "openid")]
    pub openid: Option<String>,
    #[sqlx(rename = "phone")]
    pub phone: Option<String>,
    #[serde(rename = "type")]
    #[sqlx(rename = "type")]
    pub user_type: i32,
    #[serde(rename = "desc")]
    #[sqlx(rename = "desc")]
    pub description: Option<String>,
    #[sqlx(rename = "is_paid")]
    pub is_paid: bool,
    #[sqlx(rename = "is_admin")]
    pub is_admin: bool,
    #[sqlx(rename = "paid_expires_at")]
    pub paid_expires_at: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(rename = "avatarUrl")]
    #[sqlx(rename = "avatar_url")]
    pub avatar_url: Option<String>,
    #[serde(rename = "nickname")]
    #[sqlx(rename = "nickname")]
    pub nickname: Option<String>,
}

// 管理员更新用户付费状态的请求体
#[derive(Debug, Deserialize)]
pub struct UpdatePaymentRequest {
    pub is_paid: bool,
    pub paid_expires_at: Option<String>, // ISO 8601 格式
}

// 应用状态结构体
#[derive(Clone)]
pub struct AppState {
    pub jwt_secret: String,
    pub wechat_appid: String,
    pub wechat_secret: String,
    pub free_user_data_limit: i32,
    // ===== 支付宝配置 =====
    pub alipay_app_id: Option<String>,
    pub alipay_private_key: Option<String>,
    pub alipay_alipay_public_key: Option<String>,
    pub alipay_gateway: Option<String>,
}

impl AppState {
    pub fn load() -> Result<Self, String> {
        Ok(Self {
            jwt_secret: std::env::var("JWT_SECRET")
                .map_err(|_| "环境变量JWT_SECRET未设置".to_string())?,
            wechat_appid: std::env::var("WECHAT_APPID")
                .map_err(|_| "环境变量WECHAT_APPID未设置".to_string())?,
            wechat_secret: std::env::var("WECHAT_SECRET")
                .map_err(|_| "环境变量WECHAT_SECRET未设置".to_string())?,
            free_user_data_limit: std::env::var("FREE_USER_DATA_LIMIT")
                .map(|v| v.parse().unwrap_or(20))
                .unwrap_or(20),
            // 支付宝配置（可选，未配置时使用模拟支付）
            alipay_app_id: std::env::var("ALIPAY_APP_ID").ok(),
            alipay_private_key: std::env::var("ALIPAY_PRIVATE_KEY").ok(),
            alipay_alipay_public_key: std::env::var("ALIPAY_ALIPAY_PUBLIC_KEY").ok(),
            alipay_gateway: std::env::var("ALIPAY_GATEWAY").ok(),
        })
    }
}

// ===== 支付系统 =====

/// 创建订单请求体
#[derive(Debug, Deserialize)]
pub struct CreateOrderRequest {
    pub package_type: String,
}

/// 模拟确认支付请求体
#[derive(Debug, Deserialize)]
pub struct MockConfirmRequest {
    pub order_id: String,
}

/// payment_orders 表行结构
#[derive(Debug, Serialize, FromRow)]
pub struct PaymentOrder {
    pub id: i32,
    pub user_id: i32,
    pub order_no: String,
    pub package_type: String,
    pub amount: i32,
    pub status: String,
    pub paid_at: Option<chrono::DateTime<chrono::Utc>>,
    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
    pub created_at: chrono::DateTime<chrono::Utc>,
}
