import os
import re
import mimetypes
from flask import Flask, request, jsonify, render_template, send_file, abort
import db
import indexer
from watcher import StorageWatcher

app = Flask(__name__)

@app.after_request
def add_security_headers(response):
    response.headers["Access-Control-Allow-Origin"] = "*"
    response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS, PUT, DELETE"
    response.headers["Access-Control-Allow-Headers"] = "*"
    response.headers["X-Frame-Options"] = "ALLOWALL"
    response.headers["Content-Security-Policy"] = "frame-ancestors *"
    return response

# Configuración
STORAGE_PATHS = indexer.get_configured_storage_paths()
ENABLE_WATCHER = os.getenv("ENABLE_WATCHER", "true").lower() in ("true", "1", "yes")
WATCHER_INTERVAL = int(os.getenv("WATCHER_INTERVAL", "10"))

watcher = None
if ENABLE_WATCHER:
    watcher = StorageWatcher(storage_paths=STORAGE_PATHS, interval_seconds=WATCHER_INTERVAL)
    watcher.start(run_initial_scan=True)

@app.route("/")
def index():
    """Página principal del visor integrada como iframe o standalone"""
    owner_id = request.args.get("owner_id") or request.args.get("customer_id") or request.args.get("id") or ""
    selected_year = request.args.get("year", "")
    selected_month = request.args.get("month", "")
    selected_type = request.args.get("type") or request.args.get("document_type") or ""
    
    return render_template(
        "viewer.html",
        owner_id=owner_id,
        selected_year=selected_year,
        selected_month=selected_month,
        selected_type=selected_type
    )

@app.route("/api/documents")
def api_documents():
    """Retorna los documentos (facturas, contratos, órdenes, etc.) del suscriptor"""
    owner_id = request.args.get("owner_id") or request.args.get("customer_id") or request.args.get("id")
    if not owner_id:
        return jsonify({
            "status": "success",
            "count": 0,
            "data": [],
            "message": "No se proporcionó owner_id"
        })

    year = request.args.get("year", type=int)
    month = request.args.get("month", type=int)
    document_type = request.args.get("type") or request.args.get("document_type")

    documents = db.get_invoices_by_customer(
        str(owner_id).strip(),
        year=year,
        month=month,
        document_type=document_type
    )
    return jsonify({
        "status": "success",
        "customer_id": owner_id,
        "count": len(documents),
        "data": documents
    })

@app.route("/api/customer/<customer_id>/years")
def api_customer_years(customer_id):
    """Retorna los años con documentos disponibles para el suscriptor"""
    document_type = request.args.get("type") or request.args.get("document_type")
    years = db.get_customer_years(customer_id, document_type=document_type)
    return jsonify({
        "status": "success",
        "customer_id": customer_id,
        "years": years
    })

@app.route("/api/customer/<customer_id>/types")
def api_customer_types(customer_id):
    """Retorna los tipos documentales disponibles para un suscriptor específico"""
    types = db.get_customer_document_types(customer_id)
    return jsonify({
        "status": "success",
        "customer_id": customer_id,
        "types": types
    })

@app.route("/api/types")
def api_all_types():
    """Retorna todos los tipos documentales indexados en el sistema"""
    types = db.get_all_document_types()
    return jsonify({
        "status": "success",
        "types": types
    })

@app.route("/api/documents/<int:doc_id>/file")
def api_document_file(doc_id):
    """Sirve de forma segura el archivo del documento para visualización o descarga"""
    download = request.args.get("download", "0") in ("1", "true")
    
    try:
        with db.get_connection() as conn:
            cursor = conn.execute("SELECT * FROM invoices_index WHERE id = ?", (doc_id,))
            doc = cursor.fetchone()
    except Exception as e:
        print(f"[SERVER-ERROR] Error consultando documento {doc_id} en BD: {e}")
        abort(500, description=f"Error consultando base de datos: {e}")

    if not doc:
        abort(404, description="Documento no encontrado en el índice")

    file_path = doc["file_full_path"]
    
    # Si la ruta grabada no existe físicamente, resolver dinámicamente en storage paths
    if not os.path.exists(file_path):
        resolved = None
        storage_list = STORAGE_PATHS if STORAGE_PATHS else indexer.get_configured_storage_paths()
        clean_rel = re.sub(r'^\[dir_\d+\]/', '', doc["file_relative_path"])
        
        for base_p in storage_list:
            alt_path = os.path.join(base_p, clean_rel)
            if os.path.exists(alt_path):
                resolved = alt_path
                break
            direct_file = os.path.join(base_p, doc["file_name"])
            if os.path.exists(direct_file):
                resolved = direct_file
                break
        
        if resolved:
            file_path = resolved
        else:
            print(f"[SERVER-WARN] Archivo no encontrado en disco: {file_path} ni en {storage_list}")
            abort(404, description=f"El archivo físico no existe en los volúmenes de almacenamiento: {doc['file_name']}")

    mimetype, _ = mimetypes.guess_type(file_path)
    if not mimetype:
        mimetype = "application/octet-stream"

    return send_file(
        file_path,
        mimetype=mimetype,
        as_attachment=download,
        download_name=doc["file_name"]
    )

@app.route("/api/indexer/status")
def api_indexer_status():
    """Estado y estadísticas del indexador con desglose por tipo documental"""
    stats = db.get_stats()
    stats["storage_paths"] = STORAGE_PATHS
    return jsonify({
        "status": "success",
        "stats": stats
    })

@app.route("/api/indexer/scan", methods=["POST"])
def api_indexer_scan():
    """Fuerza un escaneo manual de almacenamiento multi-documento"""
    if watcher:
        res = watcher.trigger_scan_now()
    else:
        res = indexer.scan_storage_directory(STORAGE_PATHS)
    return jsonify({
        "status": "success",
        "result": res
    })

@app.route("/api/indexer/reset", methods=["POST"])
def api_indexer_reset():
    """Limpia o recrea la base de datos y opcionalmente reindexa (?reindex=1)"""
    reindex = request.args.get("reindex", "0") in ("1", "true")
    schema = request.args.get("schema", "0") in ("1", "true")

    if schema:
        db.reset_db_schema()
    else:
        db.clear_db()

    scan_res = None
    if reindex:
        if watcher:
            scan_res = watcher.trigger_scan_now()
        else:
            scan_res = indexer.scan_storage_directory(STORAGE_PATHS)

    new_stats = db.get_stats()
    return jsonify({
        "status": "success",
        "message": "Base de datos reiniciada correctamente",
        "reindexed": reindex,
        "scan_result": scan_res,
        "stats": new_stats
    })

@app.route("/api/indexer/config", methods=["GET", "POST"])
def api_indexer_config():
    """Consulta o actualiza en caliente la configuración del observador e indexador"""
    global watcher, ENABLE_WATCHER, WATCHER_INTERVAL, STORAGE_PATHS

    if request.method == "POST":
        data = request.get_json(silent=True) or request.form or {}
        
        # 1. Intervalo
        new_interval = data.get("watcher_interval") or request.args.get("watcher_interval")
        if new_interval is not None:
            try:
                WATCHER_INTERVAL = int(new_interval)
            except ValueError:
                pass

        # 2. Habilitación del watcher
        new_enabled = data.get("enable_watcher") or request.args.get("enable_watcher")
        if new_enabled is not None:
            ENABLE_WATCHER = str(new_enabled).lower() in ("true", "1", "yes")

        # 3. Actualizar rutas en caliente si se especifican
        STORAGE_PATHS = indexer.get_configured_storage_paths()

        # 4. Actualizar instancia en memoria si existe
        if watcher:
            cfg = watcher.update_config(interval_seconds=WATCHER_INTERVAL, enabled=ENABLE_WATCHER)
        elif ENABLE_WATCHER:
            watcher = StorageWatcher(storage_paths=STORAGE_PATHS, interval_seconds=WATCHER_INTERVAL)
            watcher.start(run_initial_scan=False)

    return jsonify({
        "status": "success",
        "config": {
            "enable_watcher": ENABLE_WATCHER,
            "watcher_interval": WATCHER_INTERVAL,
            "watcher_running": bool(watcher and watcher.running),
            "storage_paths": STORAGE_PATHS
        }
    })

if __name__ == "__main__":
    port = int(os.getenv("PORT", 5541))
    app.run(host="0.0.0.0", port=port, debug=False)
