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

#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct CustomerProfile {
    pub id: Uuid,
    pub user_id: Uuid,
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub phone: Option<String>,
    pub city: Option<String>,
    pub area: Option<String>,
    pub preferred_professions: Option<Vec<String>>,
    pub active_requirement_count: i32,
    pub status: String,
    pub bio: Option<String>,
    pub custom_data: Option<serde_json::Value>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct UpsertCustomerProfilePayload {
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub phone: Option<String>,
    pub city: Option<String>,
    pub area: Option<String>,
    pub preferred_professions: Option<Vec<String>>,
    pub bio: Option<String>,
    pub custom_data: Option<serde_json::Value>,
}

pub struct CustomerRepository;

impl CustomerRepository {
    pub async fn get_by_user_id(
        pool: &PgPool,
        user_id: Uuid,
    ) -> Result<Option<CustomerProfile>, sqlx::Error> {
        let profile = sqlx::query_as::<_, CustomerProfile>(
            r#"
            SELECT
                id, user_id, first_name, last_name, phone, city, area, preferred_professions,
                active_requirement_count, status, bio, custom_data,
                created_at, updated_at
            FROM customer_profiles
            WHERE user_id = $1
            "#,
        )
        .bind(user_id)
        .fetch_optional(pool)
        .await?;

        Ok(profile)
    }

    pub async fn upsert(
        pool: &PgPool,
        user_id: Uuid,
        payload: UpsertCustomerProfilePayload,
    ) -> Result<CustomerProfile, sqlx::Error> {
        let profile = sqlx::query_as::<_, CustomerProfile>(
            r#"
            INSERT INTO customer_profiles (
                user_id, first_name, last_name, phone, city, area, preferred_professions, bio, custom_data, status
            )
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'PENDING')
            ON CONFLICT (user_id) DO UPDATE SET
                first_name = EXCLUDED.first_name,
                last_name = EXCLUDED.last_name,
                phone = EXCLUDED.phone,
                city = EXCLUDED.city,
                area = EXCLUDED.area,
                preferred_professions = EXCLUDED.preferred_professions,
                bio = EXCLUDED.bio,
                custom_data = EXCLUDED.custom_data,
                status = CASE
                    WHEN customer_profiles.status = 'APPROVED' THEN 'APPROVED'
                    ELSE 'PENDING'
                END,
                updated_at = NOW()
            RETURNING
                id, user_id, first_name, last_name, phone, city, area, preferred_professions,
                active_requirement_count, status, bio, custom_data,
                created_at, updated_at
            "#,
        )
        .bind(user_id)
        .bind(payload.first_name)
        .bind(payload.last_name)
        .bind(payload.phone)
        .bind(payload.city)
        .bind(payload.area)
        .bind(payload.preferred_professions.unwrap_or_default())
        .bind(payload.bio)
        .bind(payload.custom_data)
        .fetch_one(pool)
        .await?;

        Ok(profile)
    }

    pub async fn update_active_requirement_count(
        pool: &PgPool,
        id: Uuid,
        delta: i32,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            "UPDATE customer_profiles SET active_requirement_count = active_requirement_count + $1 WHERE id = $2",
        )
        .bind(delta)
        .bind(id)
        .execute(pool)
        .await?;
        Ok(())
    }

    pub async fn submit_for_verification(
        pool: &PgPool,
        user_id: Uuid,
    ) -> Result<CustomerProfile, sqlx::Error> {
        let profile = sqlx::query_as::<_, CustomerProfile>(
            r#"
            UPDATE customer_profiles
            SET status = 'PENDING_REVIEW', updated_at = NOW()
            WHERE user_id = $1
            RETURNING
                id, user_id, first_name, last_name, phone, city, area, preferred_professions,
                active_requirement_count, status, bio, custom_data,
                created_at, updated_at
            "#,
        )
        .bind(user_id)
        .fetch_one(pool)
        .await?;

        Ok(profile)
    }
}
