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

#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct FitnessTrainerProfile {
    pub id: Uuid,
    pub user_role_profile_id: Uuid,
    pub disciplines: Vec<String>,
    pub certifications: Vec<String>,
    pub online_sessions: bool,
    pub home_visits: bool,
    pub gym_based: bool,
    pub per_session_rate_inr: Option<i32>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpsertFitnessTrainerProfilePayload {
    pub disciplines: Vec<String>,
    pub certifications: Vec<String>,
    pub online_sessions: bool,
    pub home_visits: bool,
    pub gym_based: bool,
    pub per_session_rate_inr: Option<i32>,
}

pub struct FitnessTrainerRepository;

impl FitnessTrainerRepository {
    pub async fn get_by_user_id(pool: &PgPool, user_id: Uuid) -> Result<Option<FitnessTrainerProfile>, sqlx::Error> {
        sqlx::query_as::<_, FitnessTrainerProfile>(
            r#"SELECT ftp.id, ftp.user_role_profile_id, ftp.disciplines, ftp.certifications, ftp.online_sessions,
                      ftp.home_visits, ftp.gym_based, ftp.per_session_rate_inr,
                      ftp.created_at, ftp.updated_at
               FROM fitness_trainer_profiles ftp
               INNER JOIN user_role_profiles urp ON urp.id = ftp.user_role_profile_id
               WHERE urp.user_id = $1 AND urp.role_key = 'fitness_trainer'"#,
        )
        .bind(user_id)
        .fetch_optional(pool)
        .await
    }

    pub async fn upsert_by_user_id(pool: &PgPool, user_id: Uuid, p: UpsertFitnessTrainerProfilePayload) -> Result<FitnessTrainerProfile, sqlx::Error> {
        let user_role_profile = sqlx::query_as::<_, (Uuid,)>(
            r#"SELECT id FROM user_role_profiles WHERE user_id = $1 AND role_key = 'fitness_trainer'"#,
        )
        .bind(user_id)
        .fetch_optional(pool)
        .await?
        .ok_or(sqlx::Error::RowNotFound)?;

        sqlx::query_as::<_, FitnessTrainerProfile>(
            r#"INSERT INTO fitness_trainer_profiles (user_role_profile_id, disciplines, certifications,
                                                   online_sessions, home_visits, gym_based, per_session_rate_inr)
               VALUES ($1, $2, $3, $4, $5, $6, $7)
               ON CONFLICT (user_role_profile_id) DO UPDATE SET
                   disciplines = COALESCE(EXCLUDED.disciplines, fitness_trainer_profiles.disciplines),
                   certifications = COALESCE(EXCLUDED.certifications, fitness_trainer_profiles.certifications),
                   online_sessions = EXCLUDED.online_sessions,
                   home_visits = EXCLUDED.home_visits,
                   gym_based = EXCLUDED.gym_based,
                   per_session_rate_inr = EXCLUDED.per_session_rate_inr,
                   updated_at = NOW()
               RETURNING id, user_role_profile_id, disciplines, certifications, online_sessions,
                         home_visits, gym_based, per_session_rate_inr, created_at, updated_at"#,
        )
        .bind(user_role_profile.0)
        .bind(&p.disciplines)
        .bind(&p.certifications)
        .bind(p.online_sessions)
        .bind(p.home_visits)
        .bind(p.gym_based)
        .bind(p.per_session_rate_inr)
        .fetch_one(pool)
        .await
    }

    pub async fn get_by_user_role_id(pool: &PgPool, user_role_profile_id: Uuid) -> Result<Option<FitnessTrainerProfile>, sqlx::Error> {
        sqlx::query_as::<_, FitnessTrainerProfile>(
            r#"SELECT id, user_role_profile_id, disciplines, certifications, online_sessions,
                      home_visits, gym_based, per_session_rate_inr, created_at, updated_at
               FROM fitness_trainer_profiles WHERE user_role_profile_id = $1"#,
        )
        .bind(user_role_profile_id)
        .fetch_optional(pool)
        .await
    }

    pub async fn upsert(pool: &PgPool, user_role_profile_id: Uuid, p: UpsertFitnessTrainerProfilePayload) -> Result<FitnessTrainerProfile, sqlx::Error> {
        sqlx::query_as::<_, FitnessTrainerProfile>(
            r#"INSERT INTO fitness_trainer_profiles (user_role_profile_id, disciplines, certifications,
                                                   online_sessions, home_visits, gym_based, per_session_rate_inr)
               VALUES ($1, $2, $3, $4, $5, $6, $7)
               ON CONFLICT (user_role_profile_id) DO UPDATE SET
                   disciplines = COALESCE(EXCLUDED.disciplines, fitness_trainer_profiles.disciplines),
                   certifications = COALESCE(EXCLUDED.certifications, fitness_trainer_profiles.certifications),
                   online_sessions = EXCLUDED.online_sessions,
                   home_visits = EXCLUDED.home_visits,
                   gym_based = EXCLUDED.gym_based,
                   per_session_rate_inr = EXCLUDED.per_session_rate_inr,
                   updated_at = NOW()
               RETURNING id, user_role_profile_id, disciplines, certifications, online_sessions,
                         home_visits, gym_based, per_session_rate_inr, created_at, updated_at"#,
        )
        .bind(user_role_profile_id)
        .bind(&p.disciplines)
        .bind(&p.certifications)
        .bind(p.online_sessions)
        .bind(p.home_visits)
        .bind(p.gym_based)
        .bind(p.per_session_rate_inr)
        .fetch_one(pool)
        .await
    }
}
