use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, PgPool};
use uuid::Uuid;

#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct Application {
    pub id: Uuid,
    pub reference_number: String,
    pub job_id: Uuid,
    pub applicant_user_id: Uuid,
    pub cover_note: Option<String>,
    pub status: String,
    pub applied_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateApplicationPayload {
    pub job_id: Uuid,
    pub applicant_user_id: Uuid,
    pub cover_note: Option<String>,
}

pub struct ApplicationRepository;

impl ApplicationRepository {
    pub async fn create(
        pool: &PgPool,
        payload: CreateApplicationPayload,
    ) -> Result<Application, sqlx::Error> {
        let app = sqlx::query_as::<_, Application>(
            r#"
            INSERT INTO job_applications (job_id, applicant_user_id, cover_note)
            VALUES ($1, $2, $3)
            RETURNING *
            "#,
        )
        .bind(payload.job_id)
        .bind(payload.applicant_user_id)
        .bind(payload.cover_note)
        .fetch_one(pool)
        .await?;

        Ok(app)
    }

    pub async fn get_by_id(pool: &PgPool, id: Uuid) -> Result<Option<Application>, sqlx::Error> {
        sqlx::query_as::<_, Application>("SELECT * FROM job_applications WHERE id = $1")
            .bind(id)
            .fetch_optional(pool)
            .await
    }

    pub async fn list_by_job_id(
        pool: &PgPool,
        job_id: Uuid,
        status: Option<String>,
        page: i64,
        limit: i64,
    ) -> Result<Vec<Application>, sqlx::Error> {
        let offset = (page - 1) * limit;
        let apps = sqlx::query_as::<_, Application>(
            r#"
            SELECT * FROM job_applications
            WHERE job_id = $1 AND ($2::VARCHAR IS NULL OR status = $2)
            ORDER BY applied_at DESC
            LIMIT $3 OFFSET $4
            "#,
        )
        .bind(job_id)
        .bind(status)
        .bind(limit)
        .bind(offset)
        .fetch_all(pool)
        .await?;

        Ok(apps)
    }

    pub async fn list_by_user_id(
        pool: &PgPool,
        applicant_user_id: Uuid,
        page: i64,
        limit: i64,
    ) -> Result<Vec<Application>, sqlx::Error> {
        let offset = (page - 1) * limit;
        let apps = sqlx::query_as::<_, Application>(
            r#"
            SELECT * FROM job_applications
            WHERE applicant_user_id = $1
            ORDER BY applied_at DESC
            LIMIT $2 OFFSET $3
            "#,
        )
        .bind(applicant_user_id)
        .bind(limit)
        .bind(offset)
        .fetch_all(pool)
        .await?;
        Ok(apps)
    }

    pub async fn update_status(
        pool: &PgPool,
        id: Uuid,
        status: &str,
    ) -> Result<Application, sqlx::Error> {
        let app = sqlx::query_as::<_, Application>(
            "UPDATE job_applications SET status = $1, updated_at = NOW() WHERE id = $2 RETURNING *",
        )
        .bind(status)
        .bind(id)
        .fetch_one(pool)
        .await?;
        Ok(app)
    }
}
