# /home/Autopartes/pos/blueprints/customers_bp.py
"""Customers blueprint: CRUD, credit management, vehicles, account statements."""

import json
from flask import Blueprint, request, jsonify, g
from middleware import require_auth, has_permission
from tenant_db import get_tenant_conn
from services.audit import log_action

customers_bp = Blueprint('customers', __name__, url_prefix='/pos/api/customers')


# ─── Customer CRUD ─────────────────────────────

@customers_bp.route('', methods=['GET'])
@require_auth('customers.view')
def list_customers():
    """Search/list customers. Supports autocomplete-style search by name, RFC, phone.

    Query params:
        q: search string (matches name, RFC, phone via ILIKE)
        page: page number (default 1)
        per_page: items per page (default 50, max 200)
        branch_id: filter by branch (default: current user's branch)
    """
    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    page = int(request.args.get('page', 1))
    per_page = min(int(request.args.get('per_page', 50)), 200)
    search = request.args.get('q', '').strip()
    branch_id = request.args.get('branch_id')
    price_tier = request.args.get('price_tier', '').strip()
    status = request.args.get('status', '').strip().lower()

    where_clauses = []
    params = []

    if status == 'inactive':
        where_clauses.append("c.is_active = false")
    elif status == 'overdue':
        where_clauses.append(
            "c.is_active = true AND c.credit_limit > 0 AND c.credit_balance > c.credit_limit"
        )
    elif status == 'all':
        pass  # no is_active filter
    else:
        # Default to active customers for backwards compatibility
        where_clauses.append("c.is_active = true")

    if branch_id:
        where_clauses.append("c.branch_id = %s")
        params.append(int(branch_id))
    if search:
        where_clauses.append(
            "(c.name ILIKE %s OR c.rfc ILIKE %s OR c.phone ILIKE %s OR c.razon_social ILIKE %s)"
        )
        params.extend([f'%{search}%'] * 4)
    if price_tier:
        # Support numeric tier or Spanish labels
        tier_map = {'taller': 2, 'mostrador': 1, 'mayoreo': 3}
        tier_val = tier_map.get(price_tier.lower())
        if tier_val is None:
            try:
                tier_val = int(price_tier)
            except ValueError:
                tier_val = None
        if tier_val in (1, 2, 3):
            where_clauses.append("c.price_tier = %s")
            params.append(tier_val)

    where = " AND ".join(where_clauses) if where_clauses else "true"

    # Count
    cur.execute(f"SELECT count(*) FROM customers c WHERE {where}", params)
    total = cur.fetchone()[0]

    # Fetch
    cur.execute(f"""
        SELECT c.id, c.name, c.rfc, c.razon_social, c.phone, c.email,
               c.address, c.cp,
               c.price_tier, c.credit_limit, c.credit_balance, c.vehicle_info,
               c.branch_id, c.is_active, c.created_at,
               (SELECT MAX(s.created_at) FROM sales s WHERE s.customer_id = c.id) AS last_purchase
        FROM customers c
        WHERE {where}
        ORDER BY c.name
        LIMIT %s OFFSET %s
    """, params + [per_page, (page - 1) * per_page])

    customers = []
    for r in cur.fetchall():
        customers.append({
            'id': r[0], 'name': r[1], 'rfc': r[2], 'razon_social': r[3],
            'phone': r[4], 'email': r[5], 'address': r[6], 'cp': r[7],
            'price_tier': r[8],
            'credit_limit': float(r[9]) if r[9] else 0,
            'credit_balance': float(r[10]) if r[10] else 0,
            'vehicle_info': r[11],
            'branch_id': r[12],
            'is_active': r[13],
            'created_at': str(r[14]) if r[14] else None,
            'last_purchase': str(r[15]) if r[15] else None,
        })

    cur.close()
    conn.close()

    total_pages = (total + per_page - 1) // per_page
    return jsonify({
        'data': customers,
        'pagination': {'page': page, 'per_page': per_page, 'total': total, 'total_pages': total_pages}
    })


@customers_bp.route('/<int:customer_id>', methods=['GET'])
@require_auth('customers.view')
def get_customer(customer_id):
    """Get customer details with credit info, vehicle history, and recent purchases."""
    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    cur.execute("""
        SELECT id, branch_id, name, rfc, razon_social, regimen_fiscal, uso_cfdi,
               cp, email, phone, address, price_tier, credit_limit, credit_balance,
               is_active, vehicle_info, created_at, max_discount_pct
        FROM customers WHERE id = %s
    """, (customer_id,))
    row = cur.fetchone()
    if not row:
        cur.close(); conn.close()
        return jsonify({'error': 'Customer not found'}), 404

    cols = [desc[0] for desc in cur.description]
    customer = dict(zip(cols, row))

    # Convert Decimal to float
    for k in ('credit_limit', 'credit_balance', 'max_discount_pct'):
        if customer.get(k) is not None:
            customer[k] = float(customer[k])

    customer['created_at'] = str(customer['created_at']) if customer['created_at'] else None

    # Recent purchases (last 20)
    cur.execute("""
        SELECT s.id, s.total, s.payment_method, s.sale_type, s.status, s.created_at,
               e.name as employee_name
        FROM sales s
        LEFT JOIN employees e ON s.employee_id = e.id
        WHERE s.customer_id = %s AND s.status != 'cancelled'
        ORDER BY s.created_at DESC
        LIMIT 20
    """, (customer_id,))
    customer['recent_purchases'] = []
    for r in cur.fetchall():
        customer['recent_purchases'].append({
            'id': r[0], 'total': float(r[1]) if r[1] else 0,
            'payment_method': r[2], 'sale_type': r[3],
            'status': r[4], 'created_at': str(r[5]),
            'employee_name': r[6],
        })

    # Credit summary
    customer['credit_available'] = round(
        float(customer['credit_limit']) - float(customer['credit_balance']), 2
    )

    cur.close()
    conn.close()
    return jsonify(customer)


@customers_bp.route('/<int:customer_id>/purchases', methods=['GET'])
@require_auth('customers.view')
def get_customer_purchases(customer_id):
    """Return full purchase history for a customer."""
    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    cur.execute("""
        SELECT s.id, s.total, s.payment_method, s.sale_type, s.status, s.created_at,
               e.name as employee_name
        FROM sales s
        LEFT JOIN employees e ON s.employee_id = e.id
        WHERE s.customer_id = %s
        ORDER BY s.created_at DESC
    """, (customer_id,))

    purchases = []
    for r in cur.fetchall():
        purchases.append({
            'id': r[0],
            'total': float(r[1]) if r[1] else 0,
            'payment_method': r[2],
            'sale_type': r[3],
            'status': r[4],
            'created_at': str(r[5]),
            'employee_name': r[6],
        })

    cur.close()
    conn.close()
    return jsonify({'data': purchases})


@customers_bp.route('', methods=['POST'])
@require_auth('customers.create')
def create_customer():
    """Create a new customer.

    Body: {name, rfc, razon_social, regimen_fiscal, uso_cfdi, cp, email,
           phone, address, price_tier, credit_limit, vehicle_info}
    """
    data = request.get_json() or {}
    if not data.get('name'):
        return jsonify({'error': 'name is required'}), 400

    branch_id = data.get('branch_id', g.branch_id)

    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    try:
        cur.execute("""
            INSERT INTO customers
                (branch_id, name, rfc, razon_social, regimen_fiscal, uso_cfdi,
                 cp, email, phone, address, price_tier, credit_limit,
                 max_discount_pct, vehicle_info)
            VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            RETURNING id
        """, (
            branch_id, data['name'], data.get('rfc'), data.get('razon_social'),
            data.get('regimen_fiscal'), data.get('uso_cfdi', 'G03'),
            data.get('cp'), data.get('email'), data.get('phone'),
            data.get('address'), data.get('price_tier', 1),
            data.get('credit_limit', 0),
            data.get('max_discount_pct', 0),
            json.dumps(data['vehicle_info']) if data.get('vehicle_info') else None
        ))
        customer_id = cur.fetchone()[0]

        log_action(conn, 'CUSTOMER_CREATE', 'customer', customer_id,
                   new_value={'name': data['name'], 'rfc': data.get('rfc')})

        conn.commit()
        cur.close(); conn.close()
        return jsonify({'id': customer_id, 'message': 'Customer created'}), 201

    except Exception as e:
        conn.rollback()
        cur.close(); conn.close()
        return jsonify({'error': str(e)}), 500


@customers_bp.route('/<int:customer_id>', methods=['PUT'])
@require_auth('customers.edit')
def update_customer(customer_id):
    """Update customer fields. Credit limit changes require customers.edit_credit permission."""
    data = request.get_json() or {}
    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    # Verify customer exists
    cur.execute("SELECT id, credit_limit FROM customers WHERE id = %s", (customer_id,))
    existing = cur.fetchone()
    if not existing:
        cur.close(); conn.close()
        return jsonify({'error': 'Customer not found'}), 404

    # Credit limit change requires special permission
    if 'credit_limit' in data and float(data['credit_limit']) != float(existing[1] or 0):
        if not has_permission('customers.edit_credit'):
            cur.close(); conn.close()
            return jsonify({'error': 'Permission customers.edit_credit required to change credit limit'}), 403

        log_action(conn, 'CREDIT_CHANGE', 'customer', customer_id,
                   old_value={'credit_limit': float(existing[1] or 0)},
                   new_value={'credit_limit': float(data['credit_limit'])})

    # Build dynamic update
    allowed = ['name', 'rfc', 'razon_social', 'regimen_fiscal', 'uso_cfdi',
               'cp', 'email', 'phone', 'address', 'price_tier', 'credit_limit',
               'max_discount_pct', 'vehicle_info', 'is_active', 'branch_id']
    sets = []
    vals = []
    for field in allowed:
        if field in data:
            val = data[field]
            if field == 'vehicle_info' and isinstance(val, (dict, list)):
                val = json.dumps(val)
            sets.append(f"{field} = %s")
            vals.append(val)

    if not sets:
        cur.close(); conn.close()
        return jsonify({'error': 'No fields to update'}), 400

    vals.append(customer_id)
    cur.execute(f"UPDATE customers SET {', '.join(sets)} WHERE id = %s", vals)

    conn.commit()
    cur.close(); conn.close()
    return jsonify({'message': 'Customer updated'})


@customers_bp.route('/<int:customer_id>/statement', methods=['GET'])
@require_auth('customers.view')
def customer_statement(customer_id):
    """Account statement: sales (invoices), payments, running balance.

    Query params:
        from_date: start date (YYYY-MM-DD), default 30 days ago
        to_date: end date (YYYY-MM-DD), default today
    """
    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    from_date = request.args.get('from_date')
    to_date = request.args.get('to_date')

    # Verify customer exists
    cur.execute("SELECT name, credit_limit, credit_balance FROM customers WHERE id = %s", (customer_id,))
    cust = cur.fetchone()
    if not cust:
        cur.close(); conn.close()
        return jsonify({'error': 'Customer not found'}), 404

    where_date = ""
    params = [customer_id]
    if from_date:
        where_date += " AND s.created_at >= %s"
        params.append(from_date)
    if to_date:
        where_date += " AND s.created_at < %s::date + interval '1 day'"
        params.append(to_date)

    # Get credit sales (charges / cargos)
    cur.execute(f"""
        SELECT s.id, 'charge' as type, s.total as amount, s.created_at,
               'Venta #' || s.id as description, s.status
        FROM sales s
        WHERE s.customer_id = %s AND s.sale_type = 'credit' {where_date}
        ORDER BY s.created_at
    """, params)

    entries = []
    for r in cur.fetchall():
        entries.append({
            'id': r[0], 'type': r[1], 'amount': float(r[2]) if r[2] else 0,
            'date': str(r[3]), 'description': r[4], 'status': r[5]
        })

    # Get customer payments (abonos) from the customer_payments table
    pay_params = [customer_id]
    pay_where = ""
    if from_date:
        pay_where += " AND cp.created_at >= %s"
        pay_params.append(from_date)
    if to_date:
        pay_where += " AND cp.created_at < %s::date + interval '1 day'"
        pay_params.append(to_date)

    cur.execute(f"""
        SELECT cp.id, 'payment' as type, cp.amount, cp.created_at,
               'Abono - ' || cp.payment_method as description, 'completed' as status
        FROM customer_payments cp
        WHERE cp.customer_id = %s {pay_where}
        ORDER BY cp.created_at
    """, pay_params)

    for r in cur.fetchall():
        entries.append({
            'id': r[0], 'type': r[1], 'amount': float(r[2]) if r[2] else 0,
            'date': str(r[3]), 'description': r[4], 'status': r[5]
        })

    # Sort all entries by date for correct running balance
    entries.sort(key=lambda e: e['date'])

    # Compute running balance
    balance = 0.0
    for entry in entries:
        if entry['status'] == 'cancelled':
            continue
        if entry['type'] == 'charge':
            balance += entry['amount']
        elif entry['type'] == 'payment':
            balance -= entry['amount']
        entry['running_balance'] = round(balance, 2)

    cur.close()
    conn.close()

    return jsonify({
        'customer': {
            'id': customer_id,
            'name': cust[0],
            'credit_limit': float(cust[1]) if cust[1] else 0,
            'credit_balance': float(cust[2]) if cust[2] else 0,
        },
        'entries': entries,
        'balance': round(balance, 2),
    })


@customers_bp.route('/<int:customer_id>/vehicles', methods=['GET'])
@require_auth('customers.view')
def customer_vehicles(customer_id):
    """Get customer's vehicle list with last purchases per vehicle.

    Vehicle info is stored as JSONB in customers.vehicle_info:
    [{make, model, year, vin, plates}, ...]
    """
    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    cur.execute("SELECT vehicle_info FROM customers WHERE id = %s", (customer_id,))
    row = cur.fetchone()
    if not row:
        cur.close(); conn.close()
        return jsonify({'error': 'Customer not found'}), 404

    vehicles = row[0] or []

    # Get recent purchases for this customer to match with vehicles
    cur.execute("""
        SELECT s.id, s.total, s.created_at, s.notes,
               array_agg(si.name ORDER BY si.id) as items
        FROM sales s
        JOIN sale_items si ON si.sale_id = s.id
        WHERE s.customer_id = %s AND s.status = 'completed'
        GROUP BY s.id, s.total, s.created_at, s.notes
        ORDER BY s.created_at DESC
        LIMIT 50
    """, (customer_id,))

    recent_sales = []
    for r in cur.fetchall():
        recent_sales.append({
            'sale_id': r[0], 'total': float(r[1]) if r[1] else 0,
            'date': str(r[2]), 'notes': r[3], 'items': r[4]
        })

    cur.close()
    conn.close()

    return jsonify({
        'vehicles': vehicles,
        'recent_sales': recent_sales,
    })


@customers_bp.route('/<int:customer_id>/payment', methods=['POST'])
@require_auth('customers.edit')
def record_customer_payment(customer_id):
    """Record a payment against a customer's credit balance (abono).

    Body: {amount: float, payment_method: str, reference: str, register_id: int}
    """
    data = request.get_json() or {}
    amount = float(data.get('amount', 0))
    payment_method = data.get('payment_method', 'efectivo')
    reference = data.get('reference', '')
    register_id = data.get('register_id')

    if amount <= 0:
        return jsonify({'error': 'Amount must be greater than 0'}), 400

    conn = get_tenant_conn(g.tenant_id)
    cur = conn.cursor()

    # Verify customer exists and has a balance
    cur.execute("SELECT name, credit_balance FROM customers WHERE id = %s", (customer_id,))
    cust = cur.fetchone()
    if not cust:
        cur.close(); conn.close()
        return jsonify({'error': 'Customer not found'}), 404

    credit_balance = float(cust[1] or 0)
    if amount > credit_balance:
        cur.close(); conn.close()
        return jsonify({
            'error': f'Payment ${amount:.2f} exceeds current balance ${credit_balance:.2f}'
        }), 400

    try:
        # Record the payment
        cur.execute("""
            INSERT INTO customer_payments
                (customer_id, amount, payment_method, reference, employee_id, register_id)
            VALUES (%s,%s,%s,%s,%s,%s)
            RETURNING id, created_at
        """, (
            customer_id, amount, payment_method, reference,
            getattr(g, 'employee_id', None), register_id
        ))
        payment_id, created_at = cur.fetchone()

        # Reduce customer credit balance
        new_balance = round(credit_balance - amount, 2)
        cur.execute("""
            UPDATE customers SET credit_balance = %s WHERE id = %s
        """, (new_balance, customer_id))

        # Allocate the customer payment to oldest unpaid credit sales so the
        # aging report and per-sale balances reflect the remaining debt.
        remaining = amount
        cur.execute("""
            SELECT s.id, s.total - COALESCE(SUM(sp.amount), 0) as balance
            FROM sales s
            LEFT JOIN sale_payments sp ON sp.sale_id = s.id
            WHERE s.customer_id = %s
              AND s.sale_type = 'credit'
              AND s.status = 'completed'
            GROUP BY s.id, s.total, s.created_at
            HAVING s.total - COALESCE(SUM(sp.amount), 0) > 0
            ORDER BY s.created_at
        """, (customer_id,))
        for sale_id, balance in cur.fetchall():
            if remaining <= 0:
                break
            pay = round(min(remaining, float(balance)), 2)
            cur.execute("""
                INSERT INTO sale_payments
                    (sale_id, register_id, method, amount, reference)
                VALUES (%s, %s, %s, %s, %s)
            """, (sale_id, register_id, payment_method, pay,
                  f'Abono cliente #{customer_id}'))
            remaining = round(remaining - pay, 2)

        # Record cash movement on register if cash payment
        if register_id and payment_method == 'efectivo':
            cur.execute("""
                INSERT INTO cash_movements (register_id, type, amount, reason, employee_id)
                VALUES (%s, 'in', %s, %s, %s)
            """, (register_id, amount, f'Abono cliente #{customer_id} - {cust[0]}',
                  getattr(g, 'employee_id', None)))

        log_action(conn, 'CUSTOMER_PAYMENT', 'customer', customer_id,
                   old_value={'credit_balance': credit_balance},
                   new_value={'credit_balance': new_balance, 'payment': amount})

        conn.commit()
        cur.close(); conn.close()

        return jsonify({
            'payment_id': payment_id,
            'amount': amount,
            'previous_balance': credit_balance,
            'new_balance': new_balance,
            'created_at': str(created_at),
            'message': 'Payment recorded'
        }), 201

    except Exception as e:
        conn.rollback()
        cur.close(); conn.close()
        return jsonify({'error': str(e)}), 500
