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

#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct Job {
    pub id: Uuid,
    pub company_id: Uuid,
    pub title: String,
    pub category: Option<String>,
    pub description: String,
    pub location: String,
    pub job_type: String, // FULL_TIME, PART_TIME, CONTRACT
    pub salary_min: Option<i32>,
    pub salary_max: Option<i32>,
    pub experience_years: Option<i32>,
    pub skills: Option<Vec<String>>,
    pub status: String, // DRAFT, PENDING_APPROVAL, LIVE, EXPIRED, CLOSED, REJECTED
    pub rejection_reason: Option<String>,
    pub expires_at: Option<DateTime<Utc>>,
    pub approved_at: Option<DateTime<Utc>>,
    pub approved_by: Option<Uuid>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateJobPayload {
    pub company_id: Uuid,
    pub title: String,
    pub category: Option<String>,
    pub description: String,
    pub location: String,
    pub job_type: Option<String>,
    pub salary_min: Option<i32>,
    pub salary_max: Option<i32>,
    pub experience_years: Option<i32>,
    pub skills: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateJobPayload {
    pub title: Option<String>,
    pub category: Option<String>,
    pub description: Option<String>,
    pub location: Option<String>,
    pub job_type: Option<String>,
    pub salary_min: Option<i32>,
    pub salary_max: Option<i32>,
    pub experience_years: Option<i32>,
    pub skills: Option<Vec<String>>,
}

pub struct JobRepository;

impl JobRepository {
    pub async fn create(pool: &PgPool, payload: CreateJobPayload) -> Result<Job, sqlx::Error> {
        let job = sqlx::query_as::<_, Job>(
            r#"
            INSERT INTO jobs (
                company_id, title, category, description, location,
                job_type, salary_min, salary_max, experience_years, skills
            )
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
            RETURNING *
            "#,
        )
        .bind(payload.company_id)
        .bind(payload.title)
        .bind(payload.category)
        .bind(payload.description)
        .bind(payload.location)
        .bind(payload.job_type.unwrap_or_else(|| "FULL_TIME".to_string()))
        .bind(payload.salary_min)
        .bind(payload.salary_max)
        .bind(payload.experience_years)
        .bind(payload.skills.unwrap_or_default())
        .fetch_one(pool)
        .await?;

        Ok(job)
    }

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

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

        Ok(jobs)
    }

    pub async fn update(
        pool: &PgPool,
        id: Uuid,
        payload: UpdateJobPayload,
    ) -> Result<Job, sqlx::Error> {
        let job = sqlx::query_as::<_, Job>(
            r#"
            UPDATE jobs SET
                title = COALESCE($1, title),
                category = COALESCE($2, category),
                description = COALESCE($3, description),
                location = COALESCE($4, location),
                job_type = COALESCE($5, job_type),
                salary_min = COALESCE($6, salary_min),
                salary_max = COALESCE($7, salary_max),
                experience_years = COALESCE($8, experience_years),
                skills = COALESCE($9, skills),
                updated_at = NOW()
            WHERE id = $10
            RETURNING *
            "#,
        )
        .bind(payload.title)
        .bind(payload.category)
        .bind(payload.description)
        .bind(payload.location)
        .bind(payload.job_type)
        .bind(payload.salary_min)
        .bind(payload.salary_max)
        .bind(payload.experience_years)
        .bind(payload.skills)
        .bind(id)
        .fetch_one(pool)
        .await?;

        Ok(job)
    }

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

    pub async fn approve(
        pool: &PgPool,
        id: Uuid,
        admin_user_id: Uuid,
    ) -> Result<Job, sqlx::Error> {
        let job = sqlx::query_as::<_, Job>(
            r#"
            UPDATE jobs
            SET status = 'LIVE', approved_at = NOW(), approved_by = $1, rejection_reason = NULL, updated_at = NOW()
            WHERE id = $2
            RETURNING *
            "#,
        )
        .bind(admin_user_id)
        .bind(id)
        .fetch_one(pool)
        .await?;
        Ok(job)
    }

    pub async fn reject(
        pool: &PgPool,
        id: Uuid,
        reason: Option<String>,
    ) -> Result<Job, sqlx::Error> {
        let job = sqlx::query_as::<_, Job>(
            r#"
            UPDATE jobs
            SET status = 'REJECTED', rejection_reason = $1, approved_at = NULL, approved_by = NULL, updated_at = NOW()
            WHERE id = $2
            RETURNING *
            "#,
        )
        .bind(reason)
        .bind(id)
        .fetch_one(pool)
        .await?;
        Ok(job)
    }

    pub async fn count_by_company_id_this_month(
        pool: &PgPool,
        company_id: Uuid,
    ) -> Result<i64, sqlx::Error> {
        let count = sqlx::query_scalar::<_, i64>(
            r#"
            SELECT COUNT(*)
            FROM jobs
            WHERE company_id = $1
            AND created_at >= date_trunc('month', now())
            AND status != 'REJECTED'
            "#,
        )
        .bind(company_id)
        .fetch_one(pool)
        .await?;

        Ok(count)
    }
}
