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

use crate::db::Database;
use crate::models::{Project, Report, ReportProject, ReportService, ReportStatus, Service, TeamProfile};

use super::{ProjectType, ServiceType, TeamProfileType};

/// GraphQL enum for report status
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
pub enum ReportStatusType {
    Draft,
    Finalized,
    Paid,
}

impl From<ReportStatus> for ReportStatusType {
    fn from(status: ReportStatus) -> Self {
        match status {
            ReportStatus::Draft => ReportStatusType::Draft,
            ReportStatus::Finalized => ReportStatusType::Finalized,
            ReportStatus::Paid => ReportStatusType::Paid,
        }
    }
}

impl From<ReportStatusType> for ReportStatus {
    fn from(status: ReportStatusType) -> Self {
        match status {
            ReportStatusType::Draft => ReportStatus::Draft,
            ReportStatusType::Finalized => ReportStatus::Finalized,
            ReportStatusType::Paid => ReportStatus::Paid,
        }
    }
}

/// Report GraphQL type - team member pay period report
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ReportType {
    pub id: Uuid,
    pub team_profile_id: Uuid,
    pub start_date: NaiveDate,
    pub end_date: NaiveDate,
    pub status: ReportStatusType,
}

#[ComplexObject]
impl ReportType {
    /// Get the team profile this report belongs to
    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))
    }

    /// Get all services in this report with their labor shares
    async fn services(&self, ctx: &Context<'_>) -> Result<Vec<ReportServiceType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let services: Vec<ReportService> = sqlx::query_as::<_, ReportService>(
            r#"
            SELECT id, report_id, service_id, labor_share, created_at
            FROM report_services
            WHERE report_id = $1
            ORDER BY created_at
            "#,
        )
        .bind(self.id)
        .fetch_all(&*pool)
        .await?;

        Ok(services.into_iter().map(ReportServiceType::from).collect())
    }

    /// Get all projects in this report with their labor shares
    async fn projects(&self, ctx: &Context<'_>) -> Result<Vec<ReportProjectType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let projects: Vec<ReportProject> = sqlx::query_as::<_, ReportProject>(
            r#"
            SELECT id, report_id, project_id, labor_share, created_at
            FROM report_projects
            WHERE report_id = $1
            ORDER BY created_at
            "#,
        )
        .bind(self.id)
        .fetch_all(&*pool)
        .await?;

        Ok(projects.into_iter().map(ReportProjectType::from).collect())
    }

    /// Total labor from all services in this report
    async fn services_total(&self, ctx: &Context<'_>) -> Result<Decimal> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let total: Option<Decimal> = sqlx::query_scalar(
            r#"
            SELECT COALESCE(SUM(labor_share), 0)
            FROM report_services
            WHERE report_id = $1
            "#,
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(total.unwrap_or_default())
    }

    /// Total labor from all projects in this report
    async fn projects_total(&self, ctx: &Context<'_>) -> Result<Decimal> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let total: Option<Decimal> = sqlx::query_scalar(
            r#"
            SELECT COALESCE(SUM(labor_share), 0)
            FROM report_projects
            WHERE report_id = $1
            "#,
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(total.unwrap_or_default())
    }

    /// Grand total labor (services + projects)
    async fn total_labor(&self, ctx: &Context<'_>) -> Result<Decimal> {
        let services = self.services_total(ctx).await?;
        let projects = self.projects_total(ctx).await?;
        Ok(services + projects)
    }

    /// Count of services in this report
    async fn service_count(&self, ctx: &Context<'_>) -> Result<i32> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let count: i64 = sqlx::query_scalar(
            r#"SELECT COUNT(*) FROM report_services WHERE report_id = $1"#,
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count as i32)
    }

    /// Count of projects in this report
    async fn project_count(&self, ctx: &Context<'_>) -> Result<i32> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let count: i64 = sqlx::query_scalar(
            r#"SELECT COUNT(*) FROM report_projects WHERE report_id = $1"#,
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count as i32)
    }
}

impl From<Report> for ReportType {
    fn from(report: Report) -> Self {
        Self {
            id: report.base.id,
            team_profile_id: report.team_profile_id,
            start_date: report.start_date,
            end_date: report.end_date,
            status: report.status.into(),
        }
    }
}

/// Report-Service entry with snapshot labor share
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ReportServiceType {
    pub id: Uuid,
    pub report_id: Uuid,
    pub service_id: Uuid,
    /// Snapshot labor share - calculated and stored when added
    pub labor_share: Decimal,
}

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

        let service = sqlx::query_as::<_, Service>(
            r#"
            SELECT id, created_at, updated_at, account_id, account_address_id, date, status, notes, calendar_event_id
            FROM services
            WHERE id = $1
            "#,
        )
        .bind(self.service_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(service.map(ServiceType::from))
    }
}

impl From<ReportService> for ReportServiceType {
    fn from(rs: ReportService) -> Self {
        Self {
            id: rs.id,
            report_id: rs.report_id,
            service_id: rs.service_id,
            labor_share: rs.labor_share,
        }
    }
}

/// Report-Project entry with snapshot labor share
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ReportProjectType {
    pub id: Uuid,
    pub report_id: Uuid,
    pub project_id: Uuid,
    /// Snapshot labor share - calculated and stored when added
    pub labor_share: Decimal,
}

#[ComplexObject]
impl ReportProjectType {
    /// Get the full project details
    async fn project(&self, ctx: &Context<'_>) -> Result<Option<ProjectType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let project = sqlx::query_as::<_, Project>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, date, status, labor, amount,
                   notes, calendar_event_id, wave_service_id, account_address_id,
                   street_address, city, state, zip_code
            FROM projects
            WHERE id = $1
            "#,
        )
        .bind(self.project_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(project.map(ProjectType::from))
    }
}

impl From<ReportProject> for ReportProjectType {
    fn from(rp: ReportProject) -> Self {
        Self {
            id: rp.id,
            report_id: rp.report_id,
            project_id: rp.project_id,
            labor_share: rp.labor_share,
        }
    }
}

/// Filter input for report queries
#[derive(InputObject, Default)]
pub struct ReportFilterInput {
    /// Filter by team profile ID
    pub team_profile_id: Option<Uuid>,
    /// Filter by status
    pub status: Option<ReportStatusType>,
    /// Filter by reports that overlap with this date
    pub date: Option<NaiveDate>,
    /// Filter by reports starting on or after this date
    pub start_date_from: Option<NaiveDate>,
    /// Filter by reports ending on or before this date
    pub end_date_to: Option<NaiveDate>,
}

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

/// Eligible service for adding to a report
/// Includes calculated labor share preview
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct EligibleServiceType {
    pub service_id: Uuid,
    pub date: NaiveDate,
    /// Calculated labor share for the team member
    pub labor_share: Decimal,
    /// Total labor rate for the location
    pub labor_total: Decimal,
    /// Number of eligible team members (excludes dispatch)
    pub team_member_count: i32,
}

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

        let service = sqlx::query_as::<_, Service>(
            r#"
            SELECT id, created_at, updated_at, account_id, account_address_id, date, status, notes, calendar_event_id
            FROM services
            WHERE id = $1
            "#,
        )
        .bind(self.service_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(service.map(ServiceType::from))
    }
}

/// Eligible project for adding to a report
/// Includes calculated labor share preview
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct EligibleProjectType {
    pub project_id: Uuid,
    pub date: NaiveDate,
    /// Calculated labor share for the team member
    pub labor_share: Decimal,
    /// Total labor from project
    pub labor_total: Decimal,
    /// Number of eligible team members (excludes dispatch)
    pub team_member_count: i32,
}

#[ComplexObject]
impl EligibleProjectType {
    /// Get the full project details
    async fn project(&self, ctx: &Context<'_>) -> Result<Option<ProjectType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let project = sqlx::query_as::<_, Project>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, date, status, labor, amount,
                   notes, calendar_event_id, wave_service_id, account_address_id,
                   street_address, city, state, zip_code
            FROM projects
            WHERE id = $1
            "#,
        )
        .bind(self.project_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(project.map(ProjectType::from))
    }
}
