import uuid
from datetime import datetime, timezone, date
from pathlib import Path

from sqlalchemy import func
from sqlalchemy.orm import joinedload

from core.config import get_db, BASE_PATH
from core.security.authentication import hash_password
from core.security.exceptions import CoreDBError, GenericError
from models import Participant, Payment, PaymentDetails
from schema.participant import ParticipantResponseSchema, ParticipantProfileResponseSchema


class ParticipantService:

    async def upload_image(self, request):
        if request.image:
            file_extension = Path(request.image.filename).suffix
            new_filename = f"{uuid.uuid4()}{file_extension}"
            file_location = BASE_PATH / f'uploads/{new_filename}'
            content = await request.image.read()
            with open(file_location, "wb") as f:
                f.write(content)
            return new_filename
        return None

    def register_new_participant(self,request):
        with get_db() as db:
            payload = request.model_dump(exclude=['password', 'confirm_password','user_type'])
            payload['hashed_password']  = hash_password(request.confirm_password)
            payload['sur_name'] = request.last_name
            try:
                new_user = Participant(**payload)
                db.add(new_user)
                db.commit()
                db.refresh(new_user)
            except Exception as e:
                db.rollback()
                raise CoreDBError(f"Could not create account: {e}")

    def calculate_age(self,dob: date) -> int:
        today = date.today()
        age = today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
        return age

    def add_participant(self, user, request):
        with get_db() as db:
            payload = request.model_dump()
            payload['parent_id'] = user['id']
            payload['sur_name'] = request.last_name
            payload['hashed_password'] = hash_password(str(uuid.uuid4()))
            try:
                new_participant = Participant(**payload)
                db.add(new_participant)
                db.commit()
                db.refresh(new_participant)
                return new_participant
            except Exception as e:
                db.rollback()
                raise CoreDBError(f"Could not create new participant: {e}")

    def fetch_current_participant(self, user):
        with get_db() as db:
            if user['user_type'] == 'parent':
                results = db.query(Participant).options(joinedload(Participant.parent)).where(Participant.parent_id == user['id'],
                                                  Participant.deleted_at.is_(None)).all()
            else:
                results = db.query(Participant).options(joinedload(Participant.parent)).where(
                    Participant.deleted_at.is_(None)).all()
            return [ParticipantResponseSchema.model_validate(result).model_dump() for result in results]

    def get_all_participant_by_parent_id(self,parent_id):
        with get_db() as db:
            results = db.query(Participant).options(joinedload(Participant.parent)).where(
                Participant.parent_id == parent_id,
                Participant.deleted_at.is_(None)).all()
            return [ParticipantResponseSchema.model_validate(result).model_dump() for result in results]


    def fetch_participant_details(self, participant_id: int):
        with get_db() as db:
            record = db.query(Participant).options(joinedload(Participant.parent)).where(
                Participant.id == participant_id,
                Participant.deleted_at.is_(None)
            ).first()
            if not record:
                raise GenericError(status_code=404, exc="Participant not found")
            return ParticipantResponseSchema.model_validate(record).model_dump()



    def update_participant_from_parent(self, participant_id, request):
        payload = request.model_dump(exclude=['parent_id',])
        payload['sur_name'] = request.last_name
        with get_db() as db:
            result = (
                db.query(Participant)
                .filter(Participant.id == participant_id, Participant.deleted_at.is_(None))
                .update(payload, synchronize_session="fetch")
            )
            db.commit()
            if result == 0:
                raise GenericError(status_code=422, exc="Could not update participant")
            
    def delete_participant(self, participant_id):
        with get_db() as db:
            participant = db.query(Participant).where(
                Participant.id == participant_id,
                Participant.deleted_at.is_(None)
            ).first()

            if not participant:
                # If no matching age group is found, raise an error
                raise GenericError(status_code=404, exc="Participant not found")

            participant.deleted_at = datetime.now(timezone.utc)
            db.commit()
            return {"message": "Participant deleted successfully"}


    # from participant self account
    def update_participant_from_self_user(self, participant_id, request):
        payload = request.model_dump(exclude=['parent_id'])
        payload['sur_name'] = request.last_name
        with get_db() as db:
            result = (
                db.query(Participant)
                .filter(Participant.id == participant_id, Participant.deleted_at.is_(None))
                .update(payload, synchronize_session="fetch")
            )
            db.commit()
            if result == 0:
                raise GenericError(status_code=422, exc="Could not update participant")

    def get_participant_profile(self, participant_id):
        with get_db() as db:
            participant = db.query(Participant).where(
                Participant.id == participant_id,
                Participant.deleted_at.is_(None)
            ).first()
            if participant:
                data= ParticipantProfileResponseSchema.model_validate(participant).model_dump()
                used_wallet_sum = (
                    db.query(func.sum(Payment.used_wallet_amount))
                    .filter(
                        Payment.user_type == 'participant',
                        Payment.created_by == participant_id
                    )
                    .scalar()
                )

                # Handle None case (if no records found)
                used_wallet_sum = used_wallet_sum or 0
                data['used_wallet_amount'] = used_wallet_sum

                return data
            else:
                return None
    


    def get_all_participant(self):
        with get_db() as db:
            participants = db.query(Participant).filter(Participant.deleted_at.is_(None)).all()
            return [ParticipantProfileResponseSchema.model_validate(participant) for participant in participants]
