use crate::AppService;
use crate::error::AppError;
use chrono::Utc;
use models::repos::repo;
use models::users::user;
use sea_orm::prelude::*;
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::{IntoParams, ToSchema};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UserRepoInfo {
    pub uid: Uuid,
    pub repo_name: String,
    pub description: Option<String>,
    pub default_branch: String,
    pub is_private: bool,
    pub storage_path: String,
    pub created_at: chrono::DateTime<Utc>,
    pub updated_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UserReposResponse {
    pub username: String,
    pub repos: Vec<UserRepoInfo>,
    pub total_count: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoParams)]
pub struct UserReposQuery {
    pub page: Option<u64>,
    pub per_page: Option<u64>,
}

impl AppService {
    pub async fn get_user_repos(
        &self,
        context: Session,
        username: String,
        query: UserReposQuery,
    ) -> Result<UserReposResponse, AppError> {
        let target_user = user::Entity::find()
            .filter(user::Column::Username.eq(&username))
            .one(&self.db)
            .await?
            .ok_or(AppError::UserNotFound)?;

        let current_user_uid = context.user();

        let is_owner = current_user_uid
            .map(|uid| uid == target_user.uid)
            .unwrap_or(false);
        let has_admin_privilege = false;

        let page = std::cmp::Ord::max(query.page.unwrap_or(1), 1);
        let per_page = std::cmp::Ord::min(std::cmp::Ord::max(query.per_page.unwrap_or(20), 1), 100);
        let offset = (page - 1) * per_page;

        let mut condition = Condition::all().add(repo::Column::CreatedBy.eq(target_user.uid));

        if !is_owner && !has_admin_privilege {
            condition = condition.add(repo::Column::IsPrivate.eq(false));
        }

        let total_count = repo::Entity::find()
            .filter(condition.clone())
            .count(&self.db)
            .await?;

        let repos = repo::Entity::find()
            .filter(condition)
            .order_by_desc(repo::Column::CreatedAt)
            .limit(per_page)
            .offset(offset)
            .all(&self.db)
            .await?;

        let repo_infos: Vec<UserRepoInfo> = repos
            .into_iter()
            .map(|r| UserRepoInfo {
                uid: r.id,
                repo_name: r.repo_name,
                description: r.description,
                default_branch: r.default_branch,
                is_private: r.is_private,
                storage_path: r.storage_path,
                created_at: r.created_at,
                updated_at: r.updated_at,
            })
            .collect();

        Ok(UserReposResponse {
            username: target_user.username,
            repos: repo_infos,
            total_count,
        })
    }

    pub async fn get_current_user_repos(
        &self,
        context: Session,
        query: UserReposQuery,
    ) -> Result<UserReposResponse, AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let user = user::Entity::find()
            .filter(user::Column::Uid.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::UserNotFound)?;

        self.get_user_repos(context, user.username, query).await
    }
}
