from fastapi import  Depends, HTTPException, Request, status
import uuid
import os
from sqlalchemy.orm import Session, joinedload
from models.organization_user_inivite_model import OrganizationUserInvite, InviteStatus
from models.user_model import User, OrgRole, PlatformRole
from models.organization_model import Organization
from utils.pagination import get_paginated_response
from utils.logging import logger
from schema.organization_invite_schema import OrganizationInviteRequestSchema,AcceptInviteResponse
from datetime import datetime, timedelta, timezone
from utils.invite_hash import create_invite_token, decode_invite_token
from utils.hashing_password import hash_password
from utils.send_invite_email import send_invite_email

FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:3000")

def verify_invite_token(invite_token: str, db: Session):
    """Verify an invite token and return invite details without accepting it."""
    try:
        hashed_token = decode_invite_token(invite_token)

        invite = (
            db.query(OrganizationUserInvite)
            .filter(OrganizationUserInvite.token == hashed_token)
            .first()
        )

        if not invite:
            raise HTTPException(
                status_code=404,
                detail="Invalid or expired invitation link"
            )

        if invite.status != InviteStatus.PENDING:
            raise HTTPException(
                status_code=400,
                detail=f"This invite has already been {invite.status.value.lower()}"
            )

        now = datetime.utcnow()
        if invite.expires_at < now:
            invite.status = InviteStatus.EXPIRED
            db.commit()
            raise HTTPException(
                status_code=400,
                detail="This invitation has expired. Please ask your admin to send a new invite."
            )

        # Get org and inviter details
        org = db.query(Organization).filter(Organization.id == invite.org_id).first()
        inviter = db.query(User).filter(User.id == invite.invited_by).first()

        return {
            "email": invite.email,
            "org_name": org.name if org else "Unknown Organization",
            "org_role": invite.org_role.value if invite.org_role else "MEMBER",
            "invited_by": inviter.name if inviter else "Unknown",
            "expires_at": invite.expires_at.isoformat() if invite.expires_at else None,
        }

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

def get_all_invites(
    request: Request,
    current_user,
    db: Session,
    page:int=1,
    limit:int=10,
    search: str = None,
    status: InviteStatus = None,
    sort_by: str = "created_at",
    sort_order: str = "desc",
    org_id: str = None
):
    try:
        if current_user.org_role != OrgRole.ORG_ADMIN:
            raise HTTPException(
                status_code=403,
                detail="Only Organization Admins can view invites."
            )

        # Get org_id safely
        if org_id:
            org_id = uuid.UUID(org_id)
        else:
            org_id = current_user.org_id

        if not org_id:
            raise HTTPException(
                status_code=400,
                detail="org_id is required"
            )

        logger.info(f"Org id: {org_id} for fetching invites")

        # Base query
        invites = db.query(OrganizationUserInvite).filter(
            OrganizationUserInvite.org_id == org_id
        ).options(
            joinedload(OrganizationUserInvite.organization).load_only(Organization.name),
            joinedload(OrganizationUserInvite.inviter).load_only(User.name,
                    User.email,
                    User.platform_role
                )
        )

        # Search
        if search:
            invites = invites.filter(
                OrganizationUserInvite.email.ilike(f"%{search}%")
            )

        # Status filter
        if status:
            invites = invites.filter(
                OrganizationUserInvite.status == status
            )

        # SAFE SORTING (important fix)
        allowed_sort_fields = {
            "created_at": OrganizationUserInvite.created_at,
            "email": OrganizationUserInvite.email,
            "status": OrganizationUserInvite.status,
            "expires_at": OrganizationUserInvite.expires_at
        }

        sort_column = allowed_sort_fields.get(sort_by, OrganizationUserInvite.created_at)

        if sort_order.lower() == "desc":
            invites = invites.order_by(sort_column.desc())
        else:
            invites = invites.order_by(sort_column.asc())

        # Pagination
        paginated_invites = get_paginated_response(query=invites,request=request, page=page, limit=limit)

        return paginated_invites

    except HTTPException as e:
        raise e

    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=str(e)
        )
    

async def send_invite_to_user(request: Request,payload:OrganizationInviteRequestSchema, db: Session):
    try:
        if not payload.email or not payload.org_role:
            field = "Email" if not payload.email else "Organization role"
            message = f"{field} is required!!"
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"{message}")
        
        
        current_user=request.state.user
        if current_user.org_role != OrgRole.ORG_ADMIN:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Only Organization Admins can send invites."
            )
        current_user_domain = current_user.email.split("@")[1]
        invite_user_domain = payload.email.split("@")[1]

        if current_user_domain.lower() != invite_user_domain.lower():
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invited email must belong to the same company domain"
            )
        
        check_if_already_sent=db.query(OrganizationUserInvite).filter(OrganizationUserInvite.email==payload.email).first()
        if check_if_already_sent:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"User with same email already exits!"
            )

        # Get org name for the email
        org = db.query(Organization).filter(Organization.id == current_user.org_id).first()
        org_name = org.name if org else "your organization"
        inviter_name = current_user.name or "A team member"

        token_data = create_invite_token()
        create_new_invite=OrganizationUserInvite(
            org_id=current_user.org_id,
            email=payload.email,
            org_role=payload.org_role,
            invited_by=current_user.id,
            expires_at = datetime.now(timezone.utc) + timedelta(days=7),
            status=InviteStatus.PENDING,
            token=token_data["hashed_token"]
        )
        db.add(create_new_invite)
        #send invite email
        invite_link = f"{FRONTEND_URL}/accept-invite?token={token_data['raw_token']}"
        is_sent=await send_invite_email(
            email=payload.email,
            invite_link=invite_link,
            org_name=org_name,
            inviter_name=inviter_name
        )
        if not is_sent:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Failed to send invite email to {payload.email}"
            )
        db.commit()
        db.refresh(create_new_invite)
        return {
            "message":f'Invite sent successfully to {create_new_invite.email}!!',
        }
    except HTTPException as e:
        raise e

    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=500,
            detail=str(e)
        )
    
def accept_invite_of_user(
    payload: AcceptInviteResponse,
    invite_token: str,
    request: Request,
    db: Session
):
    try:

        # Hash incoming token
        hashed_token = decode_invite_token(invite_token)

        # Find invite
        invite = (
            db.query(OrganizationUserInvite)
            .filter(
                OrganizationUserInvite.token == hashed_token
            )
            .first()
        )

        if not invite:
            raise HTTPException(
                status_code=404,
                detail="Invalid invite token"
            )

        # Already processed
        if invite.status != InviteStatus.PENDING:
            raise HTTPException(
                status_code=400,
                detail=(
                    f"Invite already "
                    f"{invite.status.value}"
                )
            )

        # Expiry check
        now = datetime.utcnow()

        if invite.expires_at < now:
            invite.status = InviteStatus.EXPIRED

            db.commit()

            raise HTTPException(
                status_code=400,
                detail="Invite expired"
            )

        # Email ownership validation
        # if invite.email != request.state.user.email:
        #     raise HTTPException(
        #         status_code=403,
        #         detail=(
        #             "You are not authorized "
        #             "to accept this invite"
        #         )
        #     )

        # REJECT FLOW
        if payload.status.upper() == "REJECTED":

            invite.status = InviteStatus.REJECTED

            invite.token = f"invalidated_{uuid.uuid4()}"

            db.commit()

            return {
                "message": "Invite rejected successfully"
            }

        # ACCEPT VALIDATION
        if payload.status.upper() != "ACCEPTED":
            raise HTTPException(
                status_code=400,
                detail=(
                    "Status must be "
                    "'ACCEPTED' or 'REJECTED'"
                )
            )

        # Existing user check
        existing_user = (
            db.query(User)
            .filter(User.email == invite.email)
            .first()
        )

        if existing_user:
            raise HTTPException(
                status_code=400,
                detail="User already exists"
            )

        # Create user
        new_user = User(
            name=payload.name,
            email=invite.email,
            password=hash_password(payload.password),

            platform_role=PlatformRole.USER,

            org_role=invite.org_role,

            is_active=True,
            is_verified=True,

            org_id=invite.org_id
        )

        db.add(new_user)

        # Update invite
        invite.status = InviteStatus.ACCEPTED

        # invalidate token
        invite.token = f"invalidated_{uuid.uuid4()}"

        db.commit()

        db.refresh(new_user)

        return {
            "message": (
                "Invite accepted successfully"
            )
        }

    except HTTPException as e:
        db.rollback()
        raise e

    except Exception as e:
        db.rollback()

        raise HTTPException(
            status_code=500,
            detail=str(e)
        )