"""User authentication with Supabase Auth.

Uses Supabase's built-in authentication for:
- Email/password signup and login
- LinkedIn OAuth
- Session management via JWT tokens
"""
import secrets
from typing import Optional
from uuid import UUID

from fastapi import Request, Response
from loguru import logger
from supabase import create_client

from src.config import settings
from src.database import db

# Session management - using Supabase JWT tokens stored in cookies
USER_SESSION_COOKIE = "sb_session"  # Supabase session cookie
REFRESH_TOKEN_COOKIE = "sb_refresh_token"
SESSION_SECRET = settings.session_secret or secrets.token_hex(32)

# Supabase client for auth operations
_supabase_client = None

def get_supabase():
    """Get or create Supabase client."""
    global _supabase_client
    if _supabase_client is None:
        _supabase_client = create_client(settings.supabase_url, settings.supabase_key)
    return _supabase_client


class UserSession:
    """User session data."""

    def __init__(
        self,
        user_id: Optional[str] = None,
        linkedin_vanity_name: str = "",
        linkedin_name: Optional[str] = None,
        linkedin_picture: Optional[str] = None,
        email: Optional[str] = None,
        account_type: str = "ghostwriter",
        company_id: Optional[str] = None,
        onboarding_status: str = "completed",
        company_name: Optional[str] = None,
        display_name: Optional[str] = None
    ):
        self.user_id = user_id
        self.linkedin_vanity_name = linkedin_vanity_name
        self.linkedin_name = linkedin_name
        self.linkedin_picture = linkedin_picture
        self.email = email
        self.account_type = account_type
        self.company_id = company_id
        self.onboarding_status = onboarding_status
        self.company_name = company_name
        self.display_name = display_name or linkedin_name

    @property
    def is_onboarding_complete(self) -> bool:
        """Check if user has completed onboarding."""
        return self.onboarding_status == "completed"

    @property
    def is_company_owner(self) -> bool:
        """Check if user is a company owner."""
        return self.account_type == "company"

    @property
    def is_employee(self) -> bool:
        """Check if user is an employee."""
        return self.account_type == "employee"

    @property
    def is_ghostwriter(self) -> bool:
        """Check if user is a ghostwriter."""
        return self.account_type == "ghostwriter"

    def to_cookie_value(self) -> str:
        """Serialize session to cookie value."""
        import json
        import hashlib

        data = {
            "user_id": self.user_id,
            "linkedin_vanity_name": self.linkedin_vanity_name,
            "linkedin_name": self.linkedin_name,
            "linkedin_picture": self.linkedin_picture,
            "email": self.email,
            "account_type": self.account_type,
            "company_id": self.company_id,
            "onboarding_status": self.onboarding_status,
            "company_name": self.company_name,
            "display_name": self.display_name
        }

        # Create signed cookie value
        json_data = json.dumps(data)
        signature = hashlib.sha256(f"{json_data}{SESSION_SECRET}".encode()).hexdigest()[:16]

        import base64
        encoded = base64.b64encode(json_data.encode()).decode()
        return f"{encoded}.{signature}"

    @classmethod
    def from_cookie_value(cls, cookie_value: str) -> Optional["UserSession"]:
        """Deserialize session from cookie value."""
        import json
        import hashlib
        import base64

        try:
            parts = cookie_value.split(".")
            if len(parts) != 2:
                return None

            encoded, signature = parts
            json_data = base64.b64decode(encoded.encode()).decode()

            # Verify signature
            expected_sig = hashlib.sha256(f"{json_data}{SESSION_SECRET}".encode()).hexdigest()[:16]
            if signature != expected_sig:
                logger.warning("Invalid session signature")
                return None

            data = json.loads(json_data)
            return cls(
                user_id=data.get("user_id"),
                linkedin_vanity_name=data.get("linkedin_vanity_name", ""),
                linkedin_name=data.get("linkedin_name"),
                linkedin_picture=data.get("linkedin_picture"),
                email=data.get("email"),
                account_type=data.get("account_type", "ghostwriter"),
                company_id=data.get("company_id"),
                onboarding_status=data.get("onboarding_status", "completed"),
                company_name=data.get("company_name"),
                display_name=data.get("display_name")
            )
        except Exception as e:
            logger.error(f"Failed to parse session cookie: {e}")
            return None


def get_user_session(request: Request) -> Optional[UserSession]:
    """Get user session from request cookies."""
    # Try legacy cookie first (contains full session data including profile info)
    legacy_cookie = request.cookies.get("linkedin_user_session")
    if legacy_cookie:
        session = UserSession.from_cookie_value(legacy_cookie)
        if session:
            return session

    # Fallback to Supabase session validation
    access_token = request.cookies.get(USER_SESSION_COOKIE)
    if access_token:
        try:
            supabase = get_supabase()
            user_response = supabase.auth.get_user(access_token)
            if user_response and user_response.user:
                return _create_session_from_supabase_user(user_response.user)
        except Exception as e:
            logger.debug(f"Could not validate Supabase session: {e}")

    return None


async def get_user_session_async(request: Request) -> Optional[UserSession]:
    """Async version of get_user_session with profile lookup."""
    session = get_user_session(request)
    if session and session.user_id:
        # Fetch additional profile data if needed
        try:
            user = await db.get_user(UUID(session.user_id))
            if user:
                session.onboarding_status = user.onboarding_status.value if hasattr(user.onboarding_status, 'value') else user.onboarding_status
                session.account_type = user.account_type.value if hasattr(user.account_type, 'value') else user.account_type
                session.company_id = str(user.company_id) if user.company_id else None
        except Exception as e:
            logger.warning(f"Could not fetch profile data: {e}")
    return session


def _create_session_from_supabase_user(user) -> UserSession:
    """Create UserSession from Supabase user object."""
    user_metadata = user.user_metadata or {}

    return UserSession(
        user_id=str(user.id),
        linkedin_vanity_name=user_metadata.get("vanityName", ""),
        linkedin_name=user_metadata.get("name"),
        linkedin_picture=user_metadata.get("picture"),
        email=user.email,
        account_type=user_metadata.get("account_type", "ghostwriter"),
        company_id=None,  # Will be fetched from profile
        onboarding_status="pending",  # Will be fetched from profile
        company_name=None,
        display_name=user_metadata.get("display_name") or user_metadata.get("name")
    )


def set_user_session(response: Response, session: UserSession, access_token: str = None, refresh_token: str = None) -> None:
    """Set user session cookies."""
    if access_token:
        response.set_cookie(
            key=USER_SESSION_COOKIE,
            value=access_token,
            httponly=True,
            max_age=60 * 60,
            samesite="lax",
            secure=True
        )

    if refresh_token:
        response.set_cookie(
            key=REFRESH_TOKEN_COOKIE,
            value=refresh_token,
            httponly=True,
            max_age=60 * 60 * 24 * 7,
            samesite="lax",
            secure=True
        )

    # Also set legacy cookie for backwards compatibility
    response.set_cookie(
        key="linkedin_user_session",
        value=session.to_cookie_value(),
        httponly=True,
        max_age=60 * 60 * 24 * 7,
        samesite="lax",
        secure=True,
    )


def clear_user_session(response: Response) -> None:
    """Clear all session cookies."""
    response.delete_cookie(USER_SESSION_COOKIE)
    response.delete_cookie(REFRESH_TOKEN_COOKIE)
    response.delete_cookie("linkedin_user_session")


async def handle_oauth_callback(
    access_token: str,
    refresh_token: Optional[str] = None,
    allow_registration: bool = True,
    account_type: str = "ghostwriter"
) -> tuple[Optional[UserSession], Optional[str], Optional[str]]:
    """Handle OAuth callback from Supabase Auth.

    Supabase Auth handles the user creation in auth.users automatically.
    Our trigger creates the profile in the profiles table.

    Returns:
        Tuple of (UserSession, access_token, refresh_token) if authorized,
        (None, None, None) if not.
    """
    from src.database.models import Profile, AccountType, OnboardingStatus

    try:
        supabase = get_supabase()

        # Get user info using the access token
        user_response = supabase.auth.get_user(access_token)

        if not user_response or not user_response.user:
            logger.error("Failed to get user from Supabase")
            return None, None, None

        user = user_response.user
        user_metadata = user.user_metadata or {}

        import json
        logger.info(f"=== OAUTH CALLBACK ===")
        logger.info(f"user.id: {user.id}")
        logger.info(f"user.email: {user.email}")
        logger.info(f"user.user_metadata: {json.dumps(user.user_metadata, indent=2)}")

        vanity_name = user_metadata.get("vanityName")
        name = user_metadata.get("name")
        picture = user_metadata.get("picture")
        email = user.email

        logger.info(f"OAuth callback for user: {name} (vanityName={vanity_name}, email={email})")

        # Check if profile exists (should be created by trigger)
        profile = await db.get_profile(UUID(str(user.id)))

        if not profile:
            logger.info(f"Profile not found for user {user.id}, creating...")
            profile = Profile(
                account_type=AccountType(account_type),
                onboarding_status=OnboardingStatus.PENDING,
                display_name=name
            )
            profile = await db.create_profile(UUID(str(user.id)), profile)

        # Get company name if applicable
        company_name = None
        if profile.company_id:
            company = await db.get_company(profile.company_id)
            if company:
                company_name = company.name

        session = UserSession(
            user_id=str(user.id),
            linkedin_vanity_name=vanity_name or "",
            linkedin_name=name,
            linkedin_picture=picture,
            email=email,
            account_type=profile.account_type.value if hasattr(profile.account_type, 'value') else profile.account_type,
            company_id=str(profile.company_id) if profile.company_id else None,
            onboarding_status=profile.onboarding_status.value if hasattr(profile.onboarding_status, 'value') else profile.onboarding_status,
            company_name=company_name,
            display_name=profile.display_name or name
        )

        return session, access_token, refresh_token

    except Exception as e:
        logger.exception(f"OAuth callback error: {e}")
        return None, None, None


async def handle_email_password_login(email: str, password: str) -> tuple[Optional[UserSession], Optional[str], Optional[str]]:
    """Handle email/password login via Supabase Auth."""
    try:
        supabase = get_supabase()

        auth_response = supabase.auth.sign_in_with_password({
            "email": email.lower(),
            "password": password
        })

        if not auth_response or not auth_response.user:
            logger.warning(f"Failed login attempt for: {email}")
            return None, None, None

        user = auth_response.user
        session = auth_response.session

        logger.info(f"Successful email/password login for: {email}")

        # Get profile data
        profile = await db.get_profile(UUID(str(user.id)))

        if not profile:
            logger.error(f"No profile found for user {user.id}")
            return None, None, None

        # Get company name if applicable
        company_name = None
        if profile.company_id:
            company = await db.get_company(profile.company_id)
            if company:
                company_name = company.name

        user_session = UserSession(
            user_id=str(user.id),
            linkedin_vanity_name="",
            linkedin_name=profile.display_name,
            linkedin_picture=None,
            email=user.email,
            account_type=profile.account_type.value if hasattr(profile.account_type, 'value') else profile.account_type,
            company_id=str(profile.company_id) if profile.company_id else None,
            onboarding_status=profile.onboarding_status.value if hasattr(profile.onboarding_status, 'value') else profile.onboarding_status,
            company_name=company_name,
            display_name=profile.display_name
        )

        return user_session, session.access_token, session.refresh_token

    except Exception as e:
        logger.warning(f"Email/password login error: {e}")
        return None, None, None


async def create_email_password_user(
    email: str,
    password: str,
    account_type: str = "ghostwriter",
    company_id: Optional[str] = None,
    display_name: Optional[str] = None
) -> tuple[Optional[UserSession], Optional[str], Optional[str], Optional[str]]:
    """Create a new user with email/password authentication via Supabase Auth."""
    from src.database.models import Profile, AccountType, OnboardingStatus
    from src.web.user.password_auth import validate_password_strength

    try:
        # Validate password
        is_valid, error_msg = validate_password_strength(password)
        if not is_valid:
            logger.warning(f"Weak password for registration: {error_msg}")
            return None, None, None, error_msg

        supabase = get_supabase()

        # Sign up via Supabase Auth
        auth_response = supabase.auth.sign_up({
            "email": email.lower(),
            "password": password,
            "options": {
                "data": {
                    "account_type": account_type,
                    "display_name": display_name
                }
            }
        })

        if not auth_response or not auth_response.user:
            logger.warning(f"Failed to create user: {email}")
            return None, None, None, "Registrierung fehlgeschlagen"

        user = auth_response.user
        session = auth_response.session

        logger.info(f"Created new user via Supabase Auth: {user.id}")

        # Wait a moment for the trigger to create the profile
        import asyncio
        await asyncio.sleep(0.5)

        # Update profile with additional data
        profile = await db.get_profile(UUID(str(user.id)))

        if not profile:
            logger.warning(f"Profile not created by trigger, creating manually")
            acc_type = AccountType(account_type)
            profile = Profile(
                account_type=acc_type,
                display_name=display_name,
                onboarding_status=OnboardingStatus.PENDING
            )
            if company_id:
                profile.company_id = UUID(company_id)
            profile = await db.create_profile(UUID(str(user.id)), profile)
        elif company_id or display_name:
            updates = {}
            if company_id:
                updates["company_id"] = company_id
            if display_name:
                updates["display_name"] = display_name
            if updates:
                profile = await db.update_profile(UUID(str(user.id)), updates)

        # Get company name if applicable
        company_name = None
        if profile.company_id:
            company = await db.get_company(profile.company_id)
            if company:
                company_name = company.name

        user_session = UserSession(
            user_id=str(user.id),
            linkedin_vanity_name="",
            linkedin_name=display_name,
            linkedin_picture=None,
            email=user.email,
            account_type=account_type,
            company_id=company_id,
            onboarding_status="pending",
            company_name=company_name,
            display_name=display_name
        )

        access_token = session.access_token if session else None
        refresh_token = session.refresh_token if session else None

        return user_session, access_token, refresh_token, None

    except Exception as e:
        error_str = str(e)
        logger.exception(f"Error creating email/password user: {e}")

        if "already registered" in error_str.lower() or "already exists" in error_str.lower():
            return None, None, None, "Diese E-Mail-Adresse ist bereits registriert"

        return None, None, None, "Registrierung fehlgeschlagen"


async def sign_out(access_token: Optional[str] = None) -> bool:
    """Sign out user from Supabase Auth."""
    try:
        if access_token:
            supabase = get_supabase()
            supabase.auth.sign_out()
        return True
    except Exception as e:
        logger.warning(f"Error signing out: {e}")
        return False


def get_supabase_login_url(redirect_to: str) -> str:
    """Generate Supabase OAuth login URL for LinkedIn."""
    from urllib.parse import urlencode

    base_url = f"{settings.supabase_url}/auth/v1/authorize"

    params = {
        "provider": "linkedin_oidc",
        "redirect_to": redirect_to
    }

    return f"{base_url}?{urlencode(params)}"


async def refresh_session(refresh_token: str) -> tuple[Optional[str], Optional[str]]:
    """Refresh the user's session using a refresh token."""
    try:
        supabase = get_supabase()
        response = supabase.auth.refresh_session(refresh_token)

        if response and response.session:
            return response.session.access_token, response.session.refresh_token

        return None, None
    except Exception as e:
        logger.warning(f"Error refreshing session: {e}")
        return None, None
