from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.models.branch import Branch
from app.models.business import Business
from app.models.business_creation_request import BusinessCreationRequest
from app.models.membership import Membership, MembershipBranch
from app.models.user import User
from app.schemas.workspace import BusinessOnboardingRequest
from app.services.audit import record_audit


class OnboardingAlreadyCompleted(Exception):
    pass


def get_memberships_for_user(db: Session, user: User) -> list[Membership]:
    return list(db.scalars(
        select(Membership).where(
            Membership.user_id == user.id,
            Membership.is_active.is_(True),
        ).order_by(Membership.created_at))
    )


def get_membership_for_business(
    db: Session, user: User, business_id
) -> Membership | None:
    return db.scalar(select(Membership).where(
        Membership.user_id == user.id,
        Membership.business_id == business_id,
        Membership.is_active.is_(True),
    ))


def get_workspace(db: Session, user: User, business_id):
    membership = get_membership_for_business(db, user, business_id)
    if membership is None:
        return None

    business = db.get(Business, membership.business_id)
    if business is None or not business.is_active:
        return None

    if membership.all_branches:
        branches = list(
            db.scalars(
                select(Branch).where(
                    Branch.business_id == business.id,
                    Branch.is_active.is_(True),
                ).order_by(Branch.created_at)
            )
        )
    else:
        branches = list(
            db.scalars(
                select(Branch)
                .join(
                    MembershipBranch,
                    MembershipBranch.branch_id == Branch.id,
                )
                .where(
                    MembershipBranch.membership_id == membership.id,
                    Branch.business_id == business.id,
                    Branch.is_active.is_(True),
                )
                .order_by(Branch.created_at)
            )
        )

    return business, membership, branches


def get_my_workspaces(db: Session, user: User):
    workspaces = []
    for membership in get_memberships_for_user(db, user):
        workspace = get_workspace(db, user, membership.business_id)
        if workspace is not None:
            workspaces.append(workspace)
    return workspaces


def create_workspace(
    db: Session,
    user: User,
    payload: BusinessOnboardingRequest,
):
    try:
        # Lock the authenticated user's row so parallel clicks cannot both
        # observe an empty membership list and create separate workspaces.
        db.execute(select(User).where(User.id == user.id).with_for_update())
        if get_memberships_for_user(db, user):
            raise OnboardingAlreadyCompleted
        return _create_business_workspace(db, user, payload)
    except OnboardingAlreadyCompleted:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def create_additional_workspace(
    db: Session, user: User, payload: BusinessOnboardingRequest, idempotency_key: str
):
    request = BusinessCreationRequest(user_id=user.id, idempotency_key=idempotency_key)
    try:
        db.add(request)
        db.flush()
        business, membership, branches = _new_business_workspace(db, user, payload)
        request.business_id = business.id
        db.commit()
        return business, membership, branches, True
    except IntegrityError:
        db.rollback()
        existing = db.scalar(select(BusinessCreationRequest).where(
            BusinessCreationRequest.user_id == user.id,
            BusinessCreationRequest.idempotency_key == idempotency_key,
        ))
        if existing is None:
            raise
        if existing.business_id is None:
            raise
        workspace = get_workspace(db, user, existing.business_id)
        if workspace is None:
            raise
        return *workspace, False
    except Exception:
        db.rollback()
        raise


def _create_business_workspace(db: Session, user: User, payload: BusinessOnboardingRequest):
    business, membership, branches = _new_business_workspace(db, user, payload)
    db.commit()
    return business, membership, branches


def _new_business_workspace(db: Session, user: User, payload: BusinessOnboardingRequest):
    business = Business(
        name=payload.name.strip(),
        phone=_clean(payload.phone),
        email=str(payload.email).lower() if payload.email else None,
        address=_clean(payload.address),
        city=_clean(payload.city),
        state=_clean(payload.state),
        country=payload.country.strip(),
        currency=payload.currency.strip().upper(),
        default_language=payload.default_language.strip().lower(),
    )
    db.add(business)
    db.flush()
    branch = Branch(
        business_id=business.id, name=payload.main_branch_name.strip(),
        phone=business.phone, email=business.email, address=business.address,
        city=business.city, state=business.state, country=business.country,
        is_main=True, is_active=True,
    )
    membership = Membership(
        business_id=business.id, user_id=user.id, role="CEO",
        all_branches=True, is_active=True,
    )
    db.add_all([branch, membership])
    db.flush()
    record_audit(db, business.id, 'BUSINESS_CREATED', 'business', business.id, user.id, None, {
        'name': business.name, 'currency': business.currency,
    })
    record_audit(db, business.id, 'BRANCH_CREATED', 'branch', branch.id, user.id, branch.id, {
        'name': branch.name, 'is_main': branch.is_main,
    })
    return business, membership, [branch]


def _clean(value: str | None) -> str | None:
    if value is None:
        return None
    value = value.strip()
    return value or None
