from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from db.db import get_db_connection
from config.auth import role_required
import mysql.connector
from datetime import datetime
from typing import Optional, List

charge_bp = Blueprint('charge', __name__)


# ═══════════════════════════════════════════════════════════
# HELPER: Serialize a DB row dict safely
# ═══════════════════════════════════════════════════════════
def _serialize_charge(row: dict) -> dict:
    """Convert a hospital_charges row to a JSON-safe dict."""
    return {
        "id":          row.get("id"),
        "charge_name": row.get("charge_name"),
        "charge_type": row.get("charge_type"),
        "amount":      float(row.get("amount") or 0),
        "status":      row.get("status", "Active"),
        "notes":       row.get("notes"),
        "store_id":    row.get("store_id"),
        "created_by":  row.get("created_by"),
        "updated_by":  row.get("updated_by"),
        "created_at":  str(row["created_at"]) if row.get("created_at") else None,
        "updated_at":  str(row["updated_at"]) if row.get("updated_at") else None,
    }


# ═══════════════════════════════════════════════════════════
# GET /hospital_charges
# Query params: store_id (required), status, charge_type, search
# Returns all hospital charges for a branch.
# ═══════════════════════════════════════════════════════════
@charge_bp.route('/hospital_charges', methods=['GET'])
@jwt_required()
@role_required('admin', 'receptionist', 'cashier')
def get_all_charges():
    store_id    = request.args.get('store_id',    type=int)
    status      = request.args.get('status',      type=str)
    charge_type = request.args.get('charge_type', type=str)
    search      = request.args.get('search',      type=str)

    if not store_id:
        return jsonify({"success": False, "error": "store_id is required."}), 400

    conn   = None
    cursor = None
    try:
        conn   = get_db_connection()
        cursor = conn.cursor(dictionary=True)

        query  = """
            SELECT
                id,
                charge_name,
                charge_type,
                amount,
                status,
                notes,
                store_id,
                created_by,
                updated_by,
                created_at,
                updated_at
            FROM hospital_charges
            WHERE store_id = %s AND is_deleted = 0
        """
        params: List = [store_id]

        if status:
            query  += " AND status = %s"
            params.append(status)

        if charge_type:
            query  += " AND charge_type = %s"
            params.append(charge_type)

        if search:
            query  += " AND (charge_name LIKE %s OR notes LIKE %s)"
            like = f"%{search}%"
            params.extend([like, like])

        query += " ORDER BY charge_name ASC"

        cursor.execute(query, params)
        rows = cursor.fetchall()

        charges = [_serialize_charge(r) for r in rows]

        return jsonify({
            "success": True,
            "charges": charges,
            "count":   len(charges)
        }), 200

    except mysql.connector.Error as e:
        print(f"[get_all_charges] DB error: {e}")
        return jsonify({"success": False, "error": "Database error while fetching charges."}), 500
    except Exception as e:
        print(f"[get_all_charges] Unexpected error: {e}")
        return jsonify({"success": False, "error": "Unexpected server error."}), 500
    finally:
        if cursor: cursor.close()
        if conn:   conn.close()


# ═══════════════════════════════════════════════════════════
# GET /hospital_charges/<id>
# Returns a single charge by ID.
# ═══════════════════════════════════════════════════════════
@charge_bp.route('/hospital_charges/<int:charge_id>', methods=['GET'])
@jwt_required()
@role_required('admin', 'receptionist', 'cashier')
def get_charge(charge_id: int):
    conn   = None
    cursor = None
    try:
        conn   = get_db_connection()
        cursor = conn.cursor(dictionary=True)

        cursor.execute(
            """
            SELECT id, charge_name, charge_type, amount, status,
                   notes, store_id, created_by, updated_by, created_at, updated_at
            FROM hospital_charges
            WHERE id = %s AND is_deleted = 0
            """,
            (charge_id,)
        )
        row = cursor.fetchone()

        if not row:
            return jsonify({"success": False, "error": "Charge not found."}), 404

        return jsonify({"success": True, "charge": _serialize_charge(row)}), 200

    except mysql.connector.Error as e:
        print(f"[get_charge] DB error: {e}")
        return jsonify({"success": False, "error": "Database error."}), 500
    except Exception as e:
        print(f"[get_charge] Unexpected error: {e}")
        return jsonify({"success": False, "error": "Unexpected server error."}), 500
    finally:
        if cursor: cursor.close()
        if conn:   conn.close()


# ═══════════════════════════════════════════════════════════
# POST /hospital_charges
# Body: { charge_name, charge_type, amount, status,
#         notes (opt), store_id }
# ═══════════════════════════════════════════════════════════
@charge_bp.route('/hospital_charges', methods=['POST'])
@jwt_required()
@role_required('admin', 'receptionist', 'cashier')
def add_charge():
    current_user = get_jwt_identity()
    data         = request.get_json(silent=True)

    if not data:
        return jsonify({"success": False, "error": "Request body is missing or not JSON."}), 400

    # ── Required fields ───────────────────────────────────
    charge_name = (data.get("charge_name") or "").strip()
    charge_type = (data.get("charge_type") or "").strip()
    amount      = data.get("amount")
    store_id    = data.get("store_id")

    if not charge_name:
        return jsonify({"success": False, "error": "charge_name is required."}), 400
    if not charge_type:
        return jsonify({"success": False, "error": "charge_type is required."}), 400
    if amount is None:
        return jsonify({"success": False, "error": "amount is required."}), 400
    if not store_id:
        return jsonify({"success": False, "error": "store_id is required."}), 400

    try:
        amount = float(amount)
        if amount < 0:
            raise ValueError
    except (TypeError, ValueError):
        return jsonify({"success": False, "error": "amount must be a non-negative number."}), 400

    allowed_types = {"General", "Lab"}
    if charge_type not in allowed_types:
        return jsonify({
            "success": False,
            "error": f"charge_type must be one of: {', '.join(allowed_types)}."
        }), 400

    # ── Optional fields ───────────────────────────────────
    status = (data.get("status") or "Active").strip()
    notes  = (data.get("notes")  or "").strip() or None

    if status not in ("Active", "Inactive"):
        return jsonify({"success": False, "error": "status must be 'Active' or 'Inactive'."}), 400

    conn   = None
    cursor = None
    try:
        conn   = get_db_connection()
        cursor = conn.cursor(dictionary=True)

        # ── Duplicate name check within same store ─────────
        cursor.execute(
            """
            SELECT id FROM hospital_charges
            WHERE charge_name = %s AND store_id = %s AND is_deleted = 0
            """,
            (charge_name, store_id)
        )
        if cursor.fetchone():
            return jsonify({
                "success": False,
                "error": f"A charge named '{charge_name}' already exists for this branch."
            }), 409

        cursor.execute(
            """
            INSERT INTO hospital_charges
                (charge_name, charge_type, amount, status, notes,
                 store_id, created_by, created_at)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
            """,
            (
                charge_name,
                charge_type,
                amount,
                status,
                notes,
                int(store_id),
                current_user,
                datetime.now(),
            )
        )
        conn.commit()
        new_id = cursor.lastrowid

        return jsonify({
            "success":   True,
            "message":   f"Charge '{charge_name}' added successfully.",
            "charge_id": new_id
        }), 201

    except mysql.connector.Error as e:
        if conn: conn.rollback()
        print(f"[add_charge] DB error: {e}")
        return jsonify({"success": False, "error": "Database error while adding charge."}), 500
    except Exception as e:
        if conn: conn.rollback()
        print(f"[add_charge] Unexpected error: {e}")
        return jsonify({"success": False, "error": "Unexpected server error."}), 500
    finally:
        if cursor: cursor.close()
        if conn:   conn.close()


# ═══════════════════════════════════════════════════════════
# PUT /hospital_charges/<id>
# Body: { charge_name, charge_type, amount, status, notes }
# store_id cannot be changed after creation.
# ═══════════════════════════════════════════════════════════
@charge_bp.route('/hospital_charges/<int:charge_id>', methods=['PUT'])
@jwt_required()
@role_required('admin', 'receptionist', 'cashier')
def update_charge(charge_id: int):
    current_user = get_jwt_identity()
    data         = request.get_json(silent=True)

    if not data:
        return jsonify({"success": False, "error": "Request body is missing or not JSON."}), 400

    # ── Required fields ───────────────────────────────────
    charge_name = (data.get("charge_name") or "").strip()
    charge_type = (data.get("charge_type") or "").strip()
    amount      = data.get("amount")

    if not charge_name:
        return jsonify({"success": False, "error": "charge_name is required."}), 400
    if not charge_type:
        return jsonify({"success": False, "error": "charge_type is required."}), 400
    if amount is None:
        return jsonify({"success": False, "error": "amount is required."}), 400

    try:
        amount = float(amount)
        if amount < 0:
            raise ValueError
    except (TypeError, ValueError):
        return jsonify({"success": False, "error": "amount must be a non-negative number."}), 400

    allowed_types = {"General", "Lab"}
    if charge_type not in allowed_types:
        return jsonify({
            "success": False,
            "error": f"charge_type must be one of: {', '.join(allowed_types)}."
        }), 400

    status = (data.get("status") or "Active").strip()
    notes  = (data.get("notes")  or "").strip() or None

    if status not in ("Active", "Inactive"):
        return jsonify({"success": False, "error": "status must be 'Active' or 'Inactive'."}), 400

    conn   = None
    cursor = None
    try:
        conn   = get_db_connection()
        cursor = conn.cursor(dictionary=True)

        # ── Existence check ────────────────────────────────
        cursor.execute(
            "SELECT id, store_id FROM hospital_charges WHERE id = %s AND is_deleted = 0",
            (charge_id,)
        )
        existing = cursor.fetchone()
        if not existing:
            return jsonify({"success": False, "error": "Charge not found."}), 404

        # ── Duplicate name check (exclude self) ────────────
        cursor.execute(
            """
            SELECT id FROM hospital_charges
            WHERE charge_name = %s AND store_id = %s
              AND is_deleted = 0 AND id != %s
            """,
            (charge_name, existing["store_id"], charge_id)
        )
        if cursor.fetchone():
            return jsonify({
                "success": False,
                "error": f"Another charge named '{charge_name}' already exists for this branch."
            }), 409

        cursor.execute(
            """
            UPDATE hospital_charges
            SET charge_name = %s,
                charge_type = %s,
                amount      = %s,
                status      = %s,
                notes       = %s,
                updated_by  = %s,
                updated_at  = %s
            WHERE id = %s AND is_deleted = 0
            """,
            (
                charge_name,
                charge_type,
                amount,
                status,
                notes,
                current_user,
                datetime.now(),
                charge_id,
            )
        )
        conn.commit()

        if cursor.rowcount == 0:
            return jsonify({"success": False, "error": "No changes were made."}), 200

        return jsonify({
            "success": True,
            "message": f"Charge '{charge_name}' updated successfully."
        }), 200

    except mysql.connector.Error as e:
        if conn: conn.rollback()
        print(f"[update_charge] DB error: {e}")
        return jsonify({"success": False, "error": "Database error while updating charge."}), 500
    except Exception as e:
        if conn: conn.rollback()
        print(f"[update_charge] Unexpected error: {e}")
        return jsonify({"success": False, "error": "Unexpected server error."}), 500
    finally:
        if cursor: cursor.close()
        if conn:   conn.close()


# ═══════════════════════════════════════════════════════════
# DELETE /hospital_charges/<id>
# Soft-delete: sets is_deleted = 1
# ═══════════════════════════════════════════════════════════
@charge_bp.route('/hospital_charges/<int:charge_id>', methods=['DELETE'])
@jwt_required()
@role_required('admin', 'receptionist', 'cashier')
def delete_charge(charge_id: int):
    current_user = get_jwt_identity()

    conn   = None
    cursor = None
    try:
        conn   = get_db_connection()
        cursor = conn.cursor(dictionary=True)

        # ── Existence check ────────────────────────────────
        cursor.execute(
            "SELECT id, charge_name FROM hospital_charges WHERE id = %s AND is_deleted = 0",
            (charge_id,)
        )
        row = cursor.fetchone()
        if not row:
            return jsonify({"success": False, "error": "Charge not found."}), 404

        # ── Soft delete ────────────────────────────────────
        cursor.execute(
            """
            UPDATE hospital_charges
            SET is_deleted = 1,
                updated_by = %s,
                updated_at = %s
            WHERE id = %s
            """,
            (current_user, datetime.now(), charge_id)
        )
        conn.commit()

        return jsonify({
            "success": True,
            "message": f"Charge '{row['charge_name']}' deleted successfully."
        }), 200

    except mysql.connector.Error as e:
        if conn: conn.rollback()
        print(f"[delete_charge] DB error: {e}")
        return jsonify({"success": False, "error": "Database error while deleting charge."}), 500
    except Exception as e:
        if conn: conn.rollback()
        print(f"[delete_charge] Unexpected error: {e}")
        return jsonify({"success": False, "error": "Unexpected server error."}), 500
    finally:
        if cursor: cursor.close()
        if conn:   conn.close()