"""Signup bootstrap - pending registration until verify, then promote to user + workspace."""
from __future__ import annotations
import re
from datetime import datetime, timedelta, timezone
from typing import Optional
from uuid import UUID

from src.config import get_settings
from src.core.exceptions import ValidationError
from src.domain.auth.repository import PendingRegistrationRepository, UserRepository
from src.domain.auth.service import AuthService
from src.domain.billing.constants import STAFF_PLAN_SLUG
from src.domain.billing.credit_policy import email_is_staff
from src.domain.workspaces.repository import WorkspaceInviteRepository


def _normalize_email(email: str) -> str:
    return email.lower().strip()


def _validate_email(email: str) -> str:
    if not email or "@" not in email:
        raise ValueError("Invalid email")
    return _normalize_email(email)


def _sanitize_phone(phone: Optional[str]) -> Optional[str]:
    if not phone:
        return None
    s = re.sub(r"[^\d+\-() ]", "", phone.strip())
    return s[:50] if s else None


class SignupService:
    """
    Signup: create/update pending_registrations, send magic link.
    No user created until email is verified (promotion on magic/verify).
    Idempotent for existing verified users: update profile, send login link.
    """

    def __init__(
        self,
        user_repo: UserRepository,
        pending_repo: PendingRegistrationRepository,
        invite_repo: WorkspaceInviteRepository,
        auth_service: AuthService,
        plan_repo=None,
    ):
        self._user_repo = user_repo
        self._pending_repo = pending_repo
        self._invite_repo = invite_repo
        self._auth_service = auth_service
        self._plan_repo = plan_repo

    async def signup(
        self,
        email: str,
        *,
        first_name: Optional[str] = None,
        last_name: Optional[str] = None,
        phone: Optional[str] = None,
        company: Optional[str] = None,
        workspace_name: Optional[str] = None,
        plan_slug: Optional[str] = None,
        ip_address: Optional[str] = None,
        app_base_url: Optional[str] = None,
    ) -> dict:
        """
        Idempotent signup. For new users: create/update pending, send magic link.
        For existing verified users: update profile, send login link.
        Returns { ok, email_sent, user_exists, workspace_id?, next_step }.
        """
        email = _validate_email(email)
        phone = _sanitize_phone(phone)
        settings = get_settings()
        is_staff_signup = email_is_staff(email, settings.staff_email_domain_set)
        has_pending_invite = await self._invite_repo.has_pending_invite_for_email(email)

        # Staff-domain and invite-first signups are sponsored by an existing
        # platform relationship. They must never be sent through self-serve
        # plan selection or Stripe checkout.
        bypass_payment = is_staff_signup or has_pending_invite
        effective_plan_slug = STAFF_PLAN_SLUG if is_staff_signup else (
            None if has_pending_invite else plan_slug
        )

        if effective_plan_slug and self._plan_repo:
            plan = await self._plan_repo.get_by_slug(effective_plan_slug)
            if not plan or not plan.is_active:
                raise ValidationError(f"Invalid or inactive plan: {effective_plan_slug}")

        if plan_slug and plan_slug.lower() == STAFF_PLAN_SLUG and not is_staff_signup:
            domain = email.split("@")[-1] if "@" in email else ""
            if not domain or domain not in settings.staff_email_domain_set:
                raise ValidationError("Invalid or inactive plan: staff")

        user = await self._user_repo.get_by_email(email)
        user_exists = user is not None

        if user:
            # Existing verified user: update profile, send magic link (login flow)
            await self._user_repo.update_profile(
                user.id,
                first_name=first_name or user.first_name,
                last_name=last_name or user.last_name,
                phone=phone or user.phone,
                company=company or user.company,
            )
            workspace_id = None  # Frontend gets workspace from /auth/me after verify
        else:
            # New user: create/update pending (no user until verify)
            now = datetime.now(timezone.utc)
            await self._pending_repo.create_or_update(
                email,
                first_name=first_name,
                last_name=last_name,
                phone=phone,
                company=company,
                workspace_name=workspace_name,
                plan_slug=effective_plan_slug,
                magic_link_sent_at=now,
                expires_at=now + timedelta(hours=24),
            )
            workspace_id = None  # Created on promote

        token = await self._auth_service.request_magic_link(
            email,
            ip_address=ip_address,
            app_base_url=app_base_url,
            is_signup=not user_exists,
        )

        return {
            "ok": True,
            "email_sent": True,
            "user_exists": user_exists,
            "workspace_id": str(workspace_id) if workspace_id else None,
            "next_step": "verify_email",
            "requires_payment": not bypass_payment and not user_exists,
            "requires_plan_selection": not bypass_payment and not user_exists and not effective_plan_slug,
            "onboarding_reason": (
                "staff_domain" if is_staff_signup else
                "workspace_invitation" if has_pending_invite else None
            ),
        }
