"""Training Management aggregates — Phase 9.6."""
from __future__ import annotations

import uuid
from datetime import date, datetime

from sqlalchemy import Boolean, Date, DateTime, Index, Integer, Numeric, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.core.database import Base
from app.models.base import (
    ActorAuditMixin,
    OptimisticLockMixin,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import AssessmentKind, GoalStatus, GUID, ProgramStatus, WorkoutStatus


class TrainingProgram(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    __tablename__ = "training_programs"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    sport_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    coach_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    status: Mapped[ProgramStatus] = mapped_column(
        default=ProgramStatus.DRAFT, nullable=False
    )
    duration_weeks: Mapped[int | None] = mapped_column(Integer, nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "sports_center_id", "code", name="uq_training_programs_tenant_code"
        ),
        Index("ix_training_programs_center", "tenant_id", "sports_center_id"),
        Index("ix_training_programs_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Exercise(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "exercises"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    muscle_group: Mapped[str | None] = mapped_column(String(100), nullable=True)
    equipment_ref: Mapped[str | None] = mapped_column(String(100), nullable=True)
    default_sets: Mapped[int | None] = mapped_column(Integer, nullable=True)
    default_reps: Mapped[int | None] = mapped_column(Integer, nullable=True)
    default_duration_seconds: Mapped[int | None] = mapped_column(Integer, nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "sports_center_id", "code", name="uq_exercises_tenant_code"
        ),
        Index("ix_exercises_center", "tenant_id", "sports_center_id"),
        Index("ix_exercises_tenant_deleted", "tenant_id", "is_deleted"),
    )


class WorkoutPlan(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    __tablename__ = "workout_plans"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    program_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    coach_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    status: Mapped[WorkoutStatus] = mapped_column(
        default=WorkoutStatus.DRAFT, nullable=False
    )
    exercises: Mapped[list | None] = mapped_column(JSON, nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "sports_center_id", "code", name="uq_workout_plans_tenant_code"
        ),
        Index("ix_workout_plans_center", "tenant_id", "sports_center_id"),
        Index("ix_workout_plans_program", "tenant_id", "program_id"),
        Index("ix_workout_plans_tenant_deleted", "tenant_id", "is_deleted"),
    )


class WorkoutAssignment(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "workout_assignments"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    workout_plan_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    coach_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    starts_on: Mapped[date | None] = mapped_column(Date, nullable=True)
    ends_on: Mapped[date | None] = mapped_column(Date, nullable=True)
    status: Mapped[WorkoutStatus] = mapped_column(
        default=WorkoutStatus.ACTIVE, nullable=False
    )
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_workout_assignments_member", "tenant_id", "member_id"),
        Index("ix_workout_assignments_plan", "tenant_id", "workout_plan_id"),
        Index("ix_workout_assignments_tenant_deleted", "tenant_id", "is_deleted"),
    )


class TrainingGoal(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "training_goals"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    program_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    target_value: Mapped[float | None] = mapped_column(Numeric(12, 4), nullable=True)
    current_value: Mapped[float | None] = mapped_column(Numeric(12, 4), nullable=True)
    unit: Mapped[str | None] = mapped_column(String(50), nullable=True)
    target_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    status: Mapped[GoalStatus] = mapped_column(default=GoalStatus.ACTIVE, nullable=False)

    __table_args__ = (
        Index("ix_training_goals_member", "tenant_id", "member_id"),
        Index("ix_training_goals_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Measurement(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "measurements"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    metric_code: Mapped[str] = mapped_column(String(50), nullable=False)
    metric_name: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[float] = mapped_column(Numeric(12, 4), nullable=False)
    unit: Mapped[str | None] = mapped_column(String(50), nullable=True)
    measured_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    recorded_by_coach_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_measurements_member", "tenant_id", "member_id"),
        Index("ix_measurements_measured", "tenant_id", "measured_at"),
        Index("ix_measurements_tenant_deleted", "tenant_id", "is_deleted"),
    )


class ProgressRecord(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "progress_records"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    goal_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    workout_assignment_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    summary: Mapped[str | None] = mapped_column(Text, nullable=True)
    metrics: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        Index("ix_progress_records_member", "tenant_id", "member_id"),
        Index("ix_progress_records_tenant_deleted", "tenant_id", "is_deleted"),
    )


class NutritionPlan(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "nutrition_plans"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    coach_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    starts_on: Mapped[date | None] = mapped_column(Date, nullable=True)
    ends_on: Mapped[date | None] = mapped_column(Date, nullable=True)
    daily_targets: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        Index("ix_nutrition_plans_member", "tenant_id", "member_id"),
        Index("ix_nutrition_plans_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Assessment(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "assessments"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    coach_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    assessment_kind: Mapped[AssessmentKind] = mapped_column(nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    assessed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    score: Mapped[float | None] = mapped_column(Numeric(12, 4), nullable=True)
    results: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_assessments_member", "tenant_id", "member_id"),
        Index("ix_assessments_tenant_deleted", "tenant_id", "is_deleted"),
    )


class PerformanceRecord(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "performance_records"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    exercise_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    value: Mapped[float] = mapped_column(Numeric(12, 4), nullable=False)
    unit: Mapped[str | None] = mapped_column(String(50), nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_performance_records_member", "tenant_id", "member_id"),
        Index("ix_performance_records_tenant_deleted", "tenant_id", "is_deleted"),
    )


class BodyComposition(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "body_compositions"

    sports_center_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    measured_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    weight_kg: Mapped[float | None] = mapped_column(Numeric(8, 2), nullable=True)
    body_fat_percent: Mapped[float | None] = mapped_column(Numeric(6, 2), nullable=True)
    muscle_mass_kg: Mapped[float | None] = mapped_column(Numeric(8, 2), nullable=True)
    bmi: Mapped[float | None] = mapped_column(Numeric(6, 2), nullable=True)
    metrics: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_body_compositions_member", "tenant_id", "member_id"),
        Index("ix_body_compositions_tenant_deleted", "tenant_id", "is_deleted"),
    )
