use async_graphql::{ComplexObject, Context, Enum, InputObject, Result, SimpleObject};
use chrono::NaiveDate;
use uuid::Uuid;

use crate::db::Database;
use crate::models::{Account, AccountAddress, Service, ServiceTeamMember, TeamProfile, WorkStatus};

use super::{AccountAddressType, AccountType, TeamProfileType};

/// GraphQL enum for work status (services and projects)
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
pub enum WorkStatusType {
    Scheduled,
    InProgress,
    Completed,
    Cancelled,
}

impl From<WorkStatus> for WorkStatusType {
    fn from(status: WorkStatus) -> Self {
        match status {
            WorkStatus::Scheduled => WorkStatusType::Scheduled,
            WorkStatus::InProgress => WorkStatusType::InProgress,
            WorkStatus::Completed => WorkStatusType::Completed,
            WorkStatus::Cancelled => WorkStatusType::Cancelled,
        }
    }
}

impl From<WorkStatusType> for WorkStatus {
    fn from(status: WorkStatusType) -> Self {
        match status {
            WorkStatusType::Scheduled => WorkStatus::Scheduled,
            WorkStatusType::InProgress => WorkStatus::InProgress,
            WorkStatusType::Completed => WorkStatus::Completed,
            WorkStatusType::Cancelled => WorkStatus::Cancelled,
        }
    }
}

/// Reusable pagination input
#[derive(InputObject, Default)]
pub struct PaginationInput {
    /// Number of items to skip (default: 0)
    pub offset: Option<i32>,
    /// Number of items to return (default: 50, max: 100)
    pub limit: Option<i32>,
}

impl PaginationInput {
    pub fn offset(&self) -> i64 {
        self.offset.unwrap_or(0).max(0) as i64
    }

    pub fn limit(&self) -> i64 {
        self.limit.unwrap_or(50).clamp(1, 100) as i64
    }
}

/// Service GraphQL type
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ServiceType {
    pub id: Uuid,
    pub account_id: Uuid,
    pub account_address_id: Uuid,
    pub date: NaiveDate,
    pub status: WorkStatusType,
    pub notes: Option<String>,
    pub calendar_event_id: Option<String>,
}

#[ComplexObject]
impl ServiceType {
    /// Get the account for this service
    async fn account(&self, ctx: &Context<'_>) -> Result<Option<AccountType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let account = sqlx::query_as::<_, Account>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, status, start_date, end_date
            FROM accounts
            WHERE id = $1
            "#,
        )
        .bind(self.account_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(account.map(AccountType::from))
    }

    /// Get the address/location for this service
    async fn account_address(&self, ctx: &Context<'_>) -> Result<Option<AccountAddressType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let address = sqlx::query_as::<_, AccountAddress>(
            r#"
            SELECT id, created_at, updated_at, account_id, street_address, city, state, zip_code,
                   is_active, is_primary, name, notes
            FROM account_addresses
            WHERE id = $1
            "#,
        )
        .bind(self.account_address_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(address.map(AccountAddressType::from))
    }

    /// Get team members assigned to this service
    async fn team_members(&self, ctx: &Context<'_>) -> Result<Vec<ServiceTeamMemberType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let members: Vec<ServiceTeamMember> = sqlx::query_as::<_, ServiceTeamMember>(
            r#"
            SELECT id, service_id, team_profile_id, created_at
            FROM service_team_members
            WHERE service_id = $1
            "#,
        )
        .bind(self.id)
        .fetch_all(&*pool)
        .await?;

        Ok(members.into_iter().map(ServiceTeamMemberType::from).collect())
    }
}

impl From<Service> for ServiceType {
    fn from(service: Service) -> Self {
        Self {
            id: service.base.id,
            account_id: service.account_id,
            account_address_id: service.account_address_id,
            date: service.date,
            status: service.status.into(),
            notes: service.notes,
            calendar_event_id: service.calendar_event_id,
        }
    }
}

/// Service team member assignment type
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ServiceTeamMemberType {
    pub id: Uuid,
    pub service_id: Uuid,
    pub team_profile_id: Uuid,
}

#[ComplexObject]
impl ServiceTeamMemberType {
    /// Get the team profile for this assignment
    async fn team_profile(&self, ctx: &Context<'_>) -> Result<Option<TeamProfileType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let profile = sqlx::query_as::<_, TeamProfile>(
            r#"
            SELECT id, created_at, updated_at, first_name, last_name, phone, email, role, status, notes
            FROM team_profiles
            WHERE id = $1
            "#,
        )
        .bind(self.team_profile_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(profile.map(TeamProfileType::from))
    }
}

impl From<ServiceTeamMember> for ServiceTeamMemberType {
    fn from(member: ServiceTeamMember) -> Self {
        Self {
            id: member.id,
            service_id: member.service_id,
            team_profile_id: member.team_profile_id,
        }
    }
}

/// Filter input for service queries
#[derive(InputObject, Default)]
pub struct ServiceFilterInput {
    /// Filter by date range start (inclusive)
    pub date_from: Option<NaiveDate>,
    /// Filter by date range end (inclusive)
    pub date_to: Option<NaiveDate>,
    /// Filter by status
    pub status: Option<WorkStatusType>,
    /// Filter by account ID
    pub account_id: Option<Uuid>,
    /// Filter by account address ID
    pub account_address_id: Option<Uuid>,
    /// Filter by assigned team profile ID (for team member portal)
    pub team_profile_id: Option<Uuid>,
    /// Filter by customer ID (for customer portal, via account) - DEPRECATED: use customer_ids
    pub customer_id: Option<Uuid>,
    /// Filter by multiple customer IDs (for customer portal with multiple assigned customers)
    pub customer_ids: Option<Vec<Uuid>>,
}

/// Paginated service results
#[derive(SimpleObject)]
pub struct ServiceConnection {
    /// List of services
    pub items: Vec<ServiceType>,
    /// Total count of services matching the filter
    pub total_count: i32,
    /// Whether there are more items after this page
    pub has_next_page: bool,
}

/// Services grouped by assignment status for bulk assignment page
#[derive(SimpleObject)]
pub struct ServicesForAssignmentResult {
    /// Services with no team members assigned
    pub unassigned: Vec<ServiceType>,
    /// Services with dispatch (admin) assigned but no other team members
    pub ready_to_assign: Vec<ServiceType>,
    /// Services with dispatch and other team members assigned
    pub assigned: Vec<ServiceType>,
}
