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

#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct UserRoleProfile {
    pub id: Uuid,
    pub user_id: Uuid,
    pub role_key: String,
    pub display_name: Option<String>,
    pub bio: Option<String>,
    pub location: Option<String>,
    pub avatar_url: Option<String>,
    pub phone: Option<String>,
    pub email: Option<String>,
    pub status: String,
    pub verification_status: String,
    pub approval_status: String,
    pub rejection_reason: Option<String>,
    pub approved_at: Option<DateTime<Utc>>,
    pub verified_at: Option<DateTime<Utc>>,
    pub is_profile_public: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserRoleProfileWithExtension<T> {
    #[serde(flatten)]
    pub profile: UserRoleProfile,
    #[serde(flatten)]
    pub extension: T,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RoleKey {
    Photographer,
    Tutor,
    MakeupArtist,
    Developer,
    VideoEditor,
    GraphicDesigner,
    SocialMediaManager,
    FitnessTrainer,
    CateringService,
    UgcContentCreator,
    Company,
    Customer,
    Candidate,
}

impl RoleKey {
    pub fn as_str(&self) -> &'static str {
        match self {
            RoleKey::Photographer => "photographer",
            RoleKey::Tutor => "tutor",
            RoleKey::MakeupArtist => "makeup_artist",
            RoleKey::Developer => "developer",
            RoleKey::VideoEditor => "video_editor",
            RoleKey::GraphicDesigner => "graphic_designer",
            RoleKey::SocialMediaManager => "social_media_manager",
            RoleKey::FitnessTrainer => "fitness_trainer",
            RoleKey::CateringService => "catering_service",
            RoleKey::UgcContentCreator => "ugc_content_creator",
            RoleKey::Company => "company",
            RoleKey::Customer => "customer",
            RoleKey::Candidate => "candidate",
        }
    }
}

pub struct UserRoleProfileRepository;

impl UserRoleProfileRepository {
    pub async fn get_by_user_and_role(
        pool: &PgPool,
        user_id: Uuid,
        role_key: &str,
    ) -> Result<Option<UserRoleProfile>, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"SELECT id, user_id, role_key, display_name, bio, location,
                      avatar_url, phone, email, status,
                      verification_status, approval_status, rejection_reason,
                      approved_at, verified_at, is_profile_public,
                      created_at, updated_at
               FROM user_role_profiles
               WHERE user_id = $1 AND role_key = $2"#,
        )
        .bind(user_id)
        .bind(role_key)
        .fetch_optional(pool)
        .await
    }

    pub async fn get_by_id(
        pool: &PgPool,
        id: Uuid,
    ) -> Result<Option<UserRoleProfile>, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"SELECT id, user_id, role_key, display_name, bio, location,
                      avatar_url, phone, email, status,
                      verification_status, approval_status, rejection_reason,
                      approved_at, verified_at, is_profile_public,
                      created_at, updated_at
               FROM user_role_profiles
               WHERE id = $1"#,
        )
        .bind(id)
        .fetch_optional(pool)
        .await
    }

    pub async fn get_all_by_user(
        pool: &PgPool,
        user_id: Uuid,
    ) -> Result<Vec<UserRoleProfile>, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"SELECT id, user_id, role_key, display_name, bio, location,
                      avatar_url, phone, email, status,
                      verification_status, approval_status, rejection_reason,
                      approved_at, verified_at, is_profile_public,
                      created_at, updated_at
               FROM user_role_profiles
               WHERE user_id = $1"#,
        )
        .bind(user_id)
        .fetch_all(pool)
        .await
    }

    pub async fn create(
        pool: &PgPool,
        user_id: Uuid,
        role_key: &str,
    ) -> Result<UserRoleProfile, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"INSERT INTO user_role_profiles (user_id, role_key)
               VALUES ($1, $2)
               ON CONFLICT (user_id, role_key) DO UPDATE SET user_id = EXCLUDED.user_id
               RETURNING id, user_id, role_key, display_name, bio, location,
                         avatar_url, phone, email, status,
                         verification_status, approval_status, rejection_reason,
                         approved_at, verified_at, is_profile_public,
                         created_at, updated_at"#,
        )
        .bind(user_id)
        .bind(role_key)
        .fetch_one(pool)
        .await
    }

    pub async fn update(
        pool: &PgPool,
        id: Uuid,
        display_name: Option<String>,
        bio: Option<String>,
        location: Option<String>,
        avatar_url: Option<String>,
        phone: Option<String>,
        email: Option<String>,
        is_profile_public: bool,
    ) -> Result<UserRoleProfile, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"UPDATE user_role_profiles SET
                   display_name = COALESCE($2, display_name),
                   bio = COALESCE($3, bio),
                   location = COALESCE($4, location),
                   avatar_url = COALESCE($5, avatar_url),
                   phone = COALESCE($6, phone),
                   email = COALESCE($7, email),
                   is_profile_public = $8,
                   updated_at = NOW()
               WHERE id = $1
               RETURNING id, user_id, role_key, display_name, bio, location,
                         avatar_url, phone, email, status,
                         verification_status, approval_status, rejection_reason,
                         approved_at, verified_at, is_profile_public,
                         created_at, updated_at"#,
        )
        .bind(id)
        .bind(display_name)
        .bind(bio)
        .bind(location)
        .bind(avatar_url)
        .bind(phone)
        .bind(email)
        .bind(is_profile_public)
        .fetch_one(pool)
        .await
    }

    pub async fn approve(
        pool: &PgPool,
        id: Uuid,
        _approved_by: Uuid,
    ) -> Result<UserRoleProfile, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"UPDATE user_role_profiles SET
                   approval_status = 'APPROVED',
                   approved_at = NOW(),
                   updated_at = NOW()
               WHERE id = $1
               RETURNING id, user_id, role_key, display_name, bio, location,
                         avatar_url, phone, email, status,
                         verification_status, approval_status, rejection_reason,
                         approved_at, verified_at, is_profile_public,
                         created_at, updated_at"#,
        )
        .bind(id)
        .fetch_one(pool)
        .await
    }

    pub async fn reject(
        pool: &PgPool,
        id: Uuid,
        rejection_reason: &str,
    ) -> Result<UserRoleProfile, sqlx::Error> {
        sqlx::query_as::<_, UserRoleProfile>(
            r#"UPDATE user_role_profiles SET
                   approval_status = 'REJECTED',
                   rejection_reason = $2,
                   updated_at = NOW()
               WHERE id = $1
               RETURNING id, user_id, role_key, display_name, bio, location,
                         avatar_url, phone, email, status,
                         verification_status, approval_status, rejection_reason,
                         approved_at, verified_at, is_profile_public,
                         created_at, updated_at"#,
        )
        .bind(id)
        .bind(rejection_reason)
        .fetch_one(pool)
        .await
    }
}
