use actix_web::{HttpResponse, Responder, Scope, delete, get, post, put, web};
use sea_orm::{
    ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set, TransactionTrait,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::app_data::AppData;
use crate::entity::sea_orm_active_enums::{RecurrenceType, TargetType};
use crate::entity::{recurrence_rule, reminder_offset, reminder_task, reminder_task_recipient};
use crate::error::ApiError;
use crate::middleware::auth::AuthUser;
use crate::timer::WorkerCommand;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecurrenceRuleInput {
    pub r#type: String,
    pub interval: Option<i32>,
    pub by_weekday: Option<i32>,
    pub by_monthday: Option<i32>,
    pub timezone: Option<String>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OffsetInput {
    pub offset_minutes: i32,
    pub channel_inapp: Option<bool>,
    pub channel_bark: Option<bool>,
    /// Custom title for Bark push notification
    pub bark_title: Option<String>,
    /// Custom subtitle for Bark push notification
    pub bark_subtitle: Option<String>,
    /// Markdown content for Bark push (overrides body if set)
    pub bark_body_markdown: Option<String>,
    /// Alert level: active, timeSensitive, passive, critical
    pub bark_level: Option<String>,
    /// Custom icon URL for Bark push
    pub bark_icon: Option<String>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReminderTaskInput {
    pub title: String,
    pub description: Option<String>,
    pub due_at: chrono::DateTime<chrono::Utc>,
    pub recipient_ids: Vec<Uuid>,
    pub recurrence_rule: Option<RecurrenceRuleInput>,
    pub offsets: Option<Vec<OffsetInput>>,
}

#[derive(Debug, Serialize)]
pub struct OkResponse {
    pub ok: bool,
}

#[get("")]
async fn list_tasks(
    app_data: web::Data<AppData>,
    auth: AuthUser,
) -> Result<impl Responder, ApiError> {
    let items = reminder_task::Entity::find()
        .filter(reminder_task::Column::CreatorId.eq(auth.user_id))
        .order_by_desc(reminder_task::Column::DueAt)
        .find_also_related(recurrence_rule::Entity)
        .all(&app_data.db)
        .await?;

    let task_ids: Vec<Uuid> = items.iter().map(|(t, _)| t.id).collect();

    let recipients = reminder_task_recipient::Entity::find()
        .filter(reminder_task_recipient::Column::TaskId.is_in(task_ids.clone()))
        .all(&app_data.db)
        .await?;

    let offsets = reminder_offset::Entity::find()
        .filter(reminder_offset::Column::TargetType.eq(TargetType::ReminderTask))
        .filter(reminder_offset::Column::TargetId.is_in(task_ids))
        .all(&app_data.db)
        .await?;

    let result: Vec<serde_json::Value> = items
        .into_iter()
        .map(|(t, rule)| {
            let task_recipients: Vec<_> = recipients
                .iter()
                .filter(|r| r.task_id == t.id)
                .cloned()
                .collect();
            let task_offsets: Vec<_> = offsets
                .iter()
                .filter(|o| o.target_id == t.id)
                .cloned()
                .collect();
            serde_json::json!({
                "id": t.id,
                "creatorId": t.creator_id,
                "title": t.title,
                "description": t.description,
                "dueAt": t.due_at,
                "recurrenceRuleId": t.recurrence_rule_id,
                "createdAt": t.created_at,
                "updatedAt": t.updated_at,
                "recurrenceRule": rule,
                "recipients": task_recipients,
                "offsets": task_offsets,
            })
        })
        .collect();

    Ok(HttpResponse::Ok().json(result))
}

#[post("")]
async fn create_task(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    body: web::Json<ReminderTaskInput>,
) -> Result<impl Responder, ApiError> {
    if body.title.is_empty() {
        return Err(ApiError::BadRequest("Invalid payload".to_string()));
    }

    let now = chrono::Utc::now().fixed_offset();
    let body = body.into_inner();
    let user_id = auth.user_id;

    let result = app_data
        .db
        .transaction::<_, reminder_task::Model, ApiError>(|txn| {
            Box::pin(async move {
                // Create recurrence rule if provided
                let rule_id = if let Some(rule_input) = body.recurrence_rule {
                    let rule = recurrence_rule::ActiveModel {
                        id: Set(Uuid::new_v4()),
                        r#type: Set(parse_recurrence_type(&rule_input.r#type)?),
                        interval: Set(rule_input.interval.unwrap_or(1)),
                        by_weekday: Set(rule_input.by_weekday),
                        by_monthday: Set(rule_input.by_monthday),
                        timezone: Set(rule_input.timezone.unwrap_or("Asia/Shanghai".to_string())),
                        created_at: Set(now),
                        updated_at: Set(now),
                    };
                    let created = rule.insert(txn).await?;
                    Some(created.id)
                } else {
                    None
                };

                // Create task
                let new_task = reminder_task::ActiveModel {
                    id: Set(Uuid::new_v4()),
                    creator_id: Set(user_id),
                    title: Set(body.title),
                    description: Set(body.description),
                    due_at: Set(body.due_at.fixed_offset()),
                    recurrence_rule_id: Set(rule_id),
                    created_at: Set(now),
                    updated_at: Set(now),
                };
                let created_task = new_task.insert(txn).await?;

                // Create recipients
                for recipient_id in body.recipient_ids {
                    let new_recipient = reminder_task_recipient::ActiveModel {
                        task_id: Set(created_task.id),
                        user_id: Set(recipient_id),
                    };
                    new_recipient.insert(txn).await?;
                }

                // Create offsets
                if let Some(offsets) = body.offsets {
                    for offset in offsets {
                        let new_offset = reminder_offset::ActiveModel {
                            id: Set(Uuid::new_v4()),
                            target_type: Set(TargetType::ReminderTask),
                            target_id: Set(created_task.id),
                            offset_minutes: Set(offset.offset_minutes),
                            channel_inapp: Set(offset.channel_inapp.unwrap_or(true)),
                            channel_bark: Set(offset.channel_bark.unwrap_or(false)),
                            created_at: Set(now),
                            bark_title: Set(offset.bark_title),
                            bark_subtitle: Set(offset.bark_subtitle),
                            bark_body_markdown: Set(offset.bark_body_markdown),
                            bark_level: Set(offset.bark_level),
                            bark_icon: Set(offset.bark_icon),
                        };
                        new_offset.insert(txn).await?;
                    }
                }

                Ok(created_task)
            })
        })
        .await
        .map_err(|e| match e {
            sea_orm::TransactionError::Connection(e) => ApiError::Internal(e.to_string()),
            sea_orm::TransactionError::Transaction(e) => e,
        })?;

    // 触发通知生成
    let _ = app_data
        .send_worker_command(WorkerCommand::GenerateNotifications {
            target_type: TargetType::ReminderTask,
            target_id: result.id,
        })
        .await;

    Ok(HttpResponse::Ok().json(result))
}

#[get("/{id}")]
async fn get_task(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    path: web::Path<Uuid>,
) -> Result<impl Responder, ApiError> {
    let id = path.into_inner();

    let (t, rule) = reminder_task::Entity::find_by_id(id)
        .filter(reminder_task::Column::CreatorId.eq(auth.user_id))
        .find_also_related(recurrence_rule::Entity)
        .one(&app_data.db)
        .await?
        .ok_or_else(|| ApiError::NotFound("Not found".to_string()))?;

    let recipients = reminder_task_recipient::Entity::find()
        .filter(reminder_task_recipient::Column::TaskId.eq(t.id))
        .all(&app_data.db)
        .await?;

    let offsets = reminder_offset::Entity::find()
        .filter(reminder_offset::Column::TargetType.eq(TargetType::ReminderTask))
        .filter(reminder_offset::Column::TargetId.eq(t.id))
        .all(&app_data.db)
        .await?;

    let result = serde_json::json!({
        "id": t.id,
        "creatorId": t.creator_id,
        "title": t.title,
        "description": t.description,
        "dueAt": t.due_at,
        "recurrenceRuleId": t.recurrence_rule_id,
        "createdAt": t.created_at,
        "updatedAt": t.updated_at,
        "recurrenceRule": rule,
        "recipients": recipients,
        "offsets": offsets,
    });

    Ok(HttpResponse::Ok().json(result))
}

#[put("/{id}")]
async fn update_task(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    path: web::Path<Uuid>,
    body: web::Json<ReminderTaskInput>,
) -> Result<impl Responder, ApiError> {
    let id = path.into_inner();

    if body.title.is_empty() {
        return Err(ApiError::BadRequest("Invalid payload".to_string()));
    }

    let now = chrono::Utc::now().fixed_offset();
    let body = body.into_inner();
    let user_id = auth.user_id;

    let result = app_data
        .db
        .transaction::<_, reminder_task::Model, ApiError>(|txn| {
            Box::pin(async move {
                let existing = reminder_task::Entity::find_by_id(id)
                    .filter(reminder_task::Column::CreatorId.eq(user_id))
                    .one(txn)
                    .await?
                    .ok_or_else(|| ApiError::NotFound("Not found".to_string()))?;

                // Handle recurrence rule
                let mut rule_id = existing.recurrence_rule_id;
                if let Some(rule_input) = body.recurrence_rule {
                    if let Some(existing_rule_id) = rule_id {
                        // Update existing rule
                        let mut rule: recurrence_rule::ActiveModel =
                            recurrence_rule::Entity::find_by_id(existing_rule_id)
                                .one(txn)
                                .await?
                                .ok_or_else(|| ApiError::Internal("Rule not found".to_string()))?
                                .into();
                        rule.r#type = Set(parse_recurrence_type(&rule_input.r#type)?);
                        rule.interval = Set(rule_input.interval.unwrap_or(1));
                        rule.by_weekday = Set(rule_input.by_weekday);
                        rule.by_monthday = Set(rule_input.by_monthday);
                        rule.timezone =
                            Set(rule_input.timezone.unwrap_or("Asia/Shanghai".to_string()));
                        rule.updated_at = Set(now);
                        rule.update(txn).await?;
                    } else {
                        // Create new rule
                        let rule = recurrence_rule::ActiveModel {
                            id: Set(Uuid::new_v4()),
                            r#type: Set(parse_recurrence_type(&rule_input.r#type)?),
                            interval: Set(rule_input.interval.unwrap_or(1)),
                            by_weekday: Set(rule_input.by_weekday),
                            by_monthday: Set(rule_input.by_monthday),
                            timezone: Set(rule_input
                                .timezone
                                .unwrap_or("Asia/Shanghai".to_string())),
                            created_at: Set(now),
                            updated_at: Set(now),
                        };
                        let created = rule.insert(txn).await?;
                        rule_id = Some(created.id);
                    }
                } else if let Some(existing_rule_id) = rule_id {
                    // Delete existing rule
                    recurrence_rule::Entity::delete_by_id(existing_rule_id)
                        .exec(txn)
                        .await?;
                    rule_id = None;
                }

                // Delete existing recipients and create new ones
                reminder_task_recipient::Entity::delete_many()
                    .filter(reminder_task_recipient::Column::TaskId.eq(id))
                    .exec(txn)
                    .await?;

                for recipient_id in body.recipient_ids {
                    let new_recipient = reminder_task_recipient::ActiveModel {
                        task_id: Set(id),
                        user_id: Set(recipient_id),
                    };
                    new_recipient.insert(txn).await?;
                }

                // Delete existing offsets and create new ones
                reminder_offset::Entity::delete_many()
                    .filter(reminder_offset::Column::TargetType.eq(TargetType::ReminderTask))
                    .filter(reminder_offset::Column::TargetId.eq(id))
                    .exec(txn)
                    .await?;

                if let Some(offsets) = body.offsets {
                    for offset in offsets {
                        let new_offset = reminder_offset::ActiveModel {
                            id: Set(Uuid::new_v4()),
                            target_type: Set(TargetType::ReminderTask),
                            target_id: Set(id),
                            offset_minutes: Set(offset.offset_minutes),
                            channel_inapp: Set(offset.channel_inapp.unwrap_or(true)),
                            channel_bark: Set(offset.channel_bark.unwrap_or(false)),
                            created_at: Set(now),
                            bark_title: Set(offset.bark_title),
                            bark_subtitle: Set(offset.bark_subtitle),
                            bark_body_markdown: Set(offset.bark_body_markdown),
                            bark_level: Set(offset.bark_level),
                            bark_icon: Set(offset.bark_icon),
                        };
                        new_offset.insert(txn).await?;
                    }
                }

                // Update task
                let mut active: reminder_task::ActiveModel = existing.into();
                active.title = Set(body.title);
                active.description = Set(body.description);
                active.due_at = Set(body.due_at.fixed_offset());
                active.recurrence_rule_id = Set(rule_id);
                active.updated_at = Set(now);
                let updated = active.update(txn).await?;

                Ok(updated)
            })
        })
        .await
        .map_err(|e| match e {
            sea_orm::TransactionError::Connection(e) => ApiError::Internal(e.to_string()),
            sea_orm::TransactionError::Transaction(e) => e,
        })?;

    // 触发通知重新生成
    let _ = app_data
        .send_worker_command(WorkerCommand::GenerateNotifications {
            target_type: TargetType::ReminderTask,
            target_id: result.id,
        })
        .await;

    Ok(HttpResponse::Ok().json(result))
}

#[delete("/{id}")]
async fn delete_task(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    path: web::Path<Uuid>,
) -> Result<impl Responder, ApiError> {
    let id = path.into_inner();

    let result = reminder_task::Entity::delete_many()
        .filter(reminder_task::Column::Id.eq(id))
        .filter(reminder_task::Column::CreatorId.eq(auth.user_id))
        .exec(&app_data.db)
        .await?;

    if result.rows_affected == 0 {
        return Err(ApiError::NotFound("Not found".to_string()));
    }

    // Delete offsets
    reminder_offset::Entity::delete_many()
        .filter(reminder_offset::Column::TargetType.eq(TargetType::ReminderTask))
        .filter(reminder_offset::Column::TargetId.eq(id))
        .exec(&app_data.db)
        .await?;

    // Delete recipients
    reminder_task_recipient::Entity::delete_many()
        .filter(reminder_task_recipient::Column::TaskId.eq(id))
        .exec(&app_data.db)
        .await?;

    Ok(HttpResponse::Ok().json(OkResponse { ok: true }))
}

fn parse_recurrence_type(s: &str) -> Result<RecurrenceType, ApiError> {
    match s {
        "hourly" => Ok(RecurrenceType::Hourly),
        "daily" => Ok(RecurrenceType::Daily),
        "weekly" => Ok(RecurrenceType::Weekly),
        "monthly" => Ok(RecurrenceType::Monthly),
        "yearly" => Ok(RecurrenceType::Yearly),
        _ => Err(ApiError::BadRequest("Invalid recurrence type".to_string())),
    }
}

pub fn routes() -> Scope {
    web::scope("/api/reminder-tasks")
        .service(list_tasks)
        .service(create_task)
        .service(get_task)
        .service(update_task)
        .service(delete_task)
}
