#!/usr/bin/env python3
"""
Org Blueprint — Multi-compte / Organisations Pro
Sprint: HRT-82

Endpoints:
  POST   /api/v1/org                      — créer une organisation (Pro only, 1 max par owner)
  GET    /api/v1/org                      — infos org courante
  DELETE /api/v1/org                      — supprimer l'org (owner only)
  POST   /api/v1/org/invite               — inviter un membre par email (max 5 totaux)
  GET    /api/v1/org/members              — liste des membres
  DELETE /api/v1/org/members/<user_id>    — retirer un membre (owner only)

Plan enforcement:
  - Toutes les routes nécessitent plan=pro via plan_required('pro')
  - Limite : 1 org par owner, 5 membres max (owner inclus)
"""

import secrets
import logging
from datetime import datetime, timezone

from flask import Blueprint, jsonify, request

from saas_auth import require_auth as jwt_required_middleware
from org_db import get_db, migrate_org_tables

logger = logging.getLogger("turf_saas.org")

org_bp = Blueprint("org", __name__, url_prefix="/api/v1/org")

MAX_MEMBERS = 5  # max membres totaux owner inclus

# ──────────────────────────────────────────────────────────────
# Decorator: plan Pro requis
# ──────────────────────────────────────────────────────────────


def _require_pro(fn):
    """Vérifie que l'utilisateur courant est sur le plan 'pro'."""
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        user = getattr(request, "current_user", None)
        if not user:
            return jsonify({"error": "Non authentifié"}), 401
        if user.get("plan") != "pro":
            return jsonify(
                {
                    "error": "Plan insuffisant",
                    "required": "pro",
                    "current_plan": user.get("plan", "free"),
                    "upgrade_url": "/api/v1/billing/checkout",
                }
            ), 403
        return fn(*args, **kwargs)

    return wrapper


# ──────────────────────────────────────────────────────────────
# Helpers DB
# ──────────────────────────────────────────────────────────────


def _get_org_by_owner(db, owner_id: str):
    return db.execute(
        "SELECT * FROM organizations WHERE owner_id = ?", (owner_id,)
    ).fetchone()


def _get_org_by_id(db, org_id: str):
    return db.execute("SELECT * FROM organizations WHERE id = ?", (org_id,)).fetchone()


def _get_member_org(db, user_id: str):
    """Retourne l'org dont user_id est membre (owner ou member)."""
    row = db.execute(
        """SELECT o.* FROM organizations o
           JOIN org_members m ON m.org_id = o.id
           WHERE m.user_id = ?
           LIMIT 1""",
        (user_id,),
    ).fetchone()
    return row


def _count_org_members(db, org_id: str) -> int:
    row = db.execute(
        "SELECT COUNT(*) AS cnt FROM org_members WHERE org_id = ?", (org_id,)
    ).fetchone()
    return row["cnt"] if row else 0


def _get_user_by_email(db, email: str):
    """Lookup dans saas_users par email."""
    return db.execute(
        "SELECT * FROM saas_users WHERE email = ?", (email.lower().strip(),)
    ).fetchone()


def _org_to_dict(org) -> dict:
    return {
        "id": org["id"],
        "owner_id": org["owner_id"],
        "name": org["name"],
        "max_members": org["max_members"],
        "created_at": org["created_at"],
    }


def _member_to_dict(m) -> dict:
    return {
        "id": m["id"],
        "org_id": m["org_id"],
        "user_id": m["user_id"],
        "role": m["role"],
        "invited_at": m["invited_at"],
        "joined_at": m["joined_at"],
    }


# ──────────────────────────────────────────────────────────────
# POST /api/v1/org — créer une organisation
# ──────────────────────────────────────────────────────────────


@org_bp.route("", methods=["POST"])
@jwt_required_middleware
@_require_pro
def create_org():
    """
    Crée une organisation.
    ---
    tags:
      - Organisation
    security:
      - Bearer: []
    requestBody:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [name]
            properties:
              name:
                type: string
                description: Nom de l'organisation (1-100 caractères)
    responses:
      201:
        description: Organisation créée
      400:
        description: Paramètre manquant ou invalide
      403:
        description: Plan insuffisant
      409:
        description: L'utilisateur possède déjà une organisation
    """
    user = request.current_user
    owner_id = user["id"]

    data = request.get_json(silent=True) or {}
    name = (data.get("name") or "").strip()
    if not name or len(name) > 100:
        return jsonify({"error": "Le nom est requis (1-100 caractères)"}), 400

    db = get_db()
    try:
        # 1 org max par owner
        existing = _get_org_by_owner(db, owner_id)
        if existing:
            return jsonify(
                {
                    "error": "Vous possédez déjà une organisation",
                    "org_id": existing["id"],
                }
            ), 409

        org_id = secrets.token_hex(16)
        now = datetime.now(timezone.utc).isoformat()

        db.execute(
            "INSERT INTO organizations (id, owner_id, name, max_members, created_at) "
            "VALUES (?, ?, ?, ?, ?)",
            (org_id, owner_id, name, MAX_MEMBERS, now),
        )
        # Ajouter l'owner comme premier membre avec rôle 'owner'
        db.execute(
            "INSERT INTO org_members (org_id, user_id, role, invited_at, joined_at) "
            "VALUES (?, ?, 'owner', ?, ?)",
            (org_id, owner_id, now, now),
        )
        db.commit()

        org = _get_org_by_id(db, org_id)
        logger.info("Org créée: %s par user %s", org_id, owner_id)
        return jsonify({"org": _org_to_dict(org)}), 201

    except Exception as e:
        db.rollback()
        logger.error("create_org error: %s", e)
        return jsonify({"error": "Erreur interne"}), 500
    finally:
        db.close()


# ──────────────────────────────────────────────────────────────
# GET /api/v1/org — infos org courante
# ──────────────────────────────────────────────────────────────


@org_bp.route("", methods=["GET"])
@jwt_required_middleware
@_require_pro
def get_org():
    """
    Retourne l'organisation dont l'utilisateur est owner ou membre.
    ---
    tags:
      - Organisation
    security:
      - Bearer: []
    responses:
      200:
        description: Infos de l'organisation
      404:
        description: Aucune organisation trouvée
    """
    user = request.current_user
    db = get_db()
    try:
        org = _get_org_by_owner(db, user["id"]) or _get_member_org(db, user["id"])
        if not org:
            return jsonify({"error": "Aucune organisation trouvée"}), 404

        member_count = _count_org_members(db, org["id"])
        result = _org_to_dict(org)
        result["member_count"] = member_count
        return jsonify({"org": result}), 200
    finally:
        db.close()


# ──────────────────────────────────────────────────────────────
# DELETE /api/v1/org — supprimer l'organisation
# ──────────────────────────────────────────────────────────────


@org_bp.route("", methods=["DELETE"])
@jwt_required_middleware
@_require_pro
def delete_org():
    """
    Supprime l'organisation (owner uniquement).
    ---
    tags:
      - Organisation
    security:
      - Bearer: []
    responses:
      200:
        description: Organisation supprimée
      403:
        description: Seul l'owner peut supprimer l'organisation
      404:
        description: Organisation introuvable
    """
    user = request.current_user
    db = get_db()
    try:
        org = _get_org_by_owner(db, user["id"])
        if not org:
            return jsonify({"error": "Vous n'êtes pas owner d'une organisation"}), 403

        # CASCADE supprime org_members automatiquement (FK ON DELETE CASCADE)
        db.execute("DELETE FROM organizations WHERE id = ?", (org["id"],))
        db.commit()
        logger.info("Org %s supprimée par user %s", org["id"], user["id"])
        return jsonify({"ok": True, "deleted_org_id": org["id"]}), 200

    except Exception as e:
        db.rollback()
        logger.error("delete_org error: %s", e)
        return jsonify({"error": "Erreur interne"}), 500
    finally:
        db.close()


# ──────────────────────────────────────────────────────────────
# POST /api/v1/org/invite — inviter un membre par email
# ──────────────────────────────────────────────────────────────


@org_bp.route("/invite", methods=["POST"])
@jwt_required_middleware
@_require_pro
def invite_member():
    """
    Invite un utilisateur dans l'organisation par email (owner uniquement).
    Limite : 5 membres totaux (owner inclus).
    ---
    tags:
      - Organisation
    security:
      - Bearer: []
    requestBody:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [email]
            properties:
              email:
                type: string
                description: Email de l'utilisateur à inviter
    responses:
      201:
        description: Membre ajouté
      400:
        description: Paramètre manquant ou invalide
      403:
        description: Seul l'owner peut inviter / limite de membres atteinte
      404:
        description: Utilisateur introuvable ou organisation inexistante
      409:
        description: L'utilisateur est déjà membre
    """
    user = request.current_user
    data = request.get_json(silent=True) or {}
    email = (data.get("email") or "").strip().lower()

    if not email or "@" not in email:
        return jsonify({"error": "Email invalide"}), 400

    db = get_db()
    try:
        # Vérifier que l'appelant est bien owner d'une org
        org = _get_org_by_owner(db, user["id"])
        if not org:
            return jsonify({"error": "Vous n'êtes pas owner d'une organisation"}), 403

        # Vérifier la limite de membres
        current_count = _count_org_members(db, org["id"])
        if current_count >= org["max_members"]:
            return jsonify(
                {
                    "error": f"Limite de {org['max_members']} membres atteinte",
                    "current_count": current_count,
                }
            ), 403

        # Résoudre l'utilisateur cible
        target_user = _get_user_by_email(db, email)
        if not target_user:
            return jsonify({"error": "Utilisateur introuvable avec cet email"}), 404

        target_id = target_user["id"]

        # Vérifier que l'utilisateur n'est pas déjà membre de CETTE org
        existing_member = db.execute(
            "SELECT id FROM org_members WHERE org_id = ? AND user_id = ?",
            (org["id"], target_id),
        ).fetchone()
        if existing_member:
            return jsonify(
                {"error": "Cet utilisateur est déjà membre de l'organisation"}
            ), 409

        now = datetime.now(timezone.utc).isoformat()
        db.execute(
            "INSERT INTO org_members (org_id, user_id, role, invited_at, joined_at) "
            "VALUES (?, ?, 'member', ?, ?)",
            (org["id"], target_id, now, now),
        )
        db.commit()

        member_row = db.execute(
            "SELECT * FROM org_members WHERE org_id = ? AND user_id = ?",
            (org["id"], target_id),
        ).fetchone()
        logger.info(
            "User %s invité dans org %s par %s", target_id, org["id"], user["id"]
        )
        return jsonify({"member": _member_to_dict(member_row)}), 201

    except Exception as e:
        db.rollback()
        logger.error("invite_member error: %s", e)
        return jsonify({"error": "Erreur interne"}), 500
    finally:
        db.close()


# ──────────────────────────────────────────────────────────────
# GET /api/v1/org/members — liste des membres
# ──────────────────────────────────────────────────────────────


@org_bp.route("/members", methods=["GET"])
@jwt_required_middleware
@_require_pro
def list_members():
    """
    Liste les membres de l'organisation courante.
    ---
    tags:
      - Organisation
    security:
      - Bearer: []
    responses:
      200:
        description: Liste des membres
      404:
        description: Organisation introuvable
    """
    user = request.current_user
    db = get_db()
    try:
        org = _get_org_by_owner(db, user["id"]) or _get_member_org(db, user["id"])
        if not org:
            return jsonify({"error": "Aucune organisation trouvée"}), 404

        members = db.execute(
            "SELECT m.*, u.email, u.firstname, u.lastname "
            "FROM org_members m "
            "LEFT JOIN saas_users u ON u.id = m.user_id "
            "WHERE m.org_id = ? "
            "ORDER BY m.invited_at ASC",
            (org["id"],),
        ).fetchall()

        result = []
        for m in members:
            d = _member_to_dict(m)
            d["email"] = m["email"]
            d["firstname"] = m["firstname"] or ""
            d["lastname"] = m["lastname"] or ""
            result.append(d)

        return jsonify(
            {
                "org_id": org["id"],
                "members": result,
                "count": len(result),
                "max_members": org["max_members"],
            }
        ), 200
    finally:
        db.close()


# ──────────────────────────────────────────────────────────────
# DELETE /api/v1/org/members/<user_id> — retirer un membre
# ──────────────────────────────────────────────────────────────


@org_bp.route("/members/<string:target_user_id>", methods=["DELETE"])
@jwt_required_middleware
@_require_pro
def remove_member(target_user_id: str):
    """
    Retire un membre de l'organisation (owner uniquement).
    L'owner ne peut pas se retirer lui-même.
    ---
    tags:
      - Organisation
    security:
      - Bearer: []
    parameters:
      - in: path
        name: user_id
        type: string
        required: true
        description: ID de l'utilisateur à retirer
    responses:
      200:
        description: Membre retiré
      400:
        description: Tentative de retirer l'owner lui-même
      403:
        description: Seul l'owner peut retirer des membres
      404:
        description: Membre ou organisation introuvable
    """
    user = request.current_user
    db = get_db()
    try:
        org = _get_org_by_owner(db, user["id"])
        if not org:
            return jsonify({"error": "Vous n'êtes pas owner d'une organisation"}), 403

        # L'owner ne peut pas se retirer lui-même (utiliser DELETE /api/v1/org à la place)
        if target_user_id == user["id"]:
            return jsonify(
                {
                    "error": "L'owner ne peut pas se retirer lui-même. "
                    "Utilisez DELETE /api/v1/org pour supprimer l'organisation."
                }
            ), 400

        member = db.execute(
            "SELECT * FROM org_members WHERE org_id = ? AND user_id = ?",
            (org["id"], target_user_id),
        ).fetchone()
        if not member:
            return jsonify({"error": "Membre introuvable dans cette organisation"}), 404

        db.execute(
            "DELETE FROM org_members WHERE org_id = ? AND user_id = ?",
            (org["id"], target_user_id),
        )
        db.commit()
        logger.info(
            "User %s retiré de l'org %s par %s", target_user_id, org["id"], user["id"]
        )
        return jsonify({"ok": True, "removed_user_id": target_user_id}), 200

    except Exception as e:
        db.rollback()
        logger.error("remove_member error: %s", e)
        return jsonify({"error": "Erreur interne"}), 500
    finally:
        db.close()


# ──────────────────────────────────────────────────────────────
# On-import : migration idempotente
# ──────────────────────────────────────────────────────────────

try:
    migrate_org_tables()
except Exception as _e:
    logger.warning("org_db migration skipped (test env?): %s", _e)
