#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OTEC JCCD LTDA — Sistema de Gestión Integral
trabajo.otecjd.com
"""
import os, json, datetime
from functools import wraps
from flask import (Flask, request, jsonify, session,
                   render_template, redirect, url_for)
from flask_sqlalchemy import SQLAlchemy

# ── CONFIG ───────────────────────────────────────────────────
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'otecjccd-trabajo-s3cr3t-k3y-2026!')

DB_USER = os.environ.get('DB_USER', 'otecjdco_trabajo')
DB_PASS = os.environ.get('DB_PASS', 'Faby.0209$')
DB_NAME = os.environ.get('DB_NAME', 'otecjdco_trabajo')
DB_HOST = os.environ.get('DB_HOST', 'localhost')

app.config['SQLALCHEMY_DATABASE_URI'] = f'mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_HOST}/{DB_NAME}?charset=utf8mb4'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['JSON_AS_ASCII'] = False

db = SQLAlchemy(app)

# ── MODELOS ──────────────────────────────────────────────────

class Usuario(db.Model):
    __tablename__ = 'usuarios'
    id       = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(255), nullable=False)
    nombre   = db.Column(db.String(120), nullable=False)
    rol      = db.Column(db.Enum('superadmin','admin','ejecutor'), default='ejecutor')
    activo   = db.Column(db.Boolean, default=True)

    def to_dict(self):
        return {'id': self.id, 'username': self.username,
                'nombre': self.nombre, 'rol': self.rol, 'activo': self.activo}

class Tarea(db.Model):
    __tablename__ = 'tareas'
    id              = db.Column(db.Integer, primary_key=True)
    institucion     = db.Column(db.String(200), nullable=False)
    n_participantes = db.Column(db.Integer, default=0)
    monto_disponible = db.Column(db.Numeric(12,2), default=0)
    monto_ofertado  = db.Column(db.Numeric(12,2), default=0)
    fecha_cierre1   = db.Column(db.Date)
    fecha_cierre2   = db.Column(db.Date)
    asignado_a      = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    estado          = db.Column(db.Enum('pendiente','en_progreso','completada','cancelada'), default='pendiente')
    prioridad       = db.Column(db.Enum('baja','media','alta','urgente'), default='media')
    notas           = db.Column(db.Text)
    created_by      = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at      = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at      = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)

    asignado  = db.relationship('Usuario', foreign_keys=[asignado_a])
    creador   = db.relationship('Usuario', foreign_keys=[created_by])

    def to_dict(self):
        return {
            'id': self.id, 'institucion': self.institucion,
            'n_participantes': self.n_participantes,
            'monto_disponible': float(self.monto_disponible or 0),
            'monto_ofertado': float(self.monto_ofertado or 0),
            'fecha_cierre1': str(self.fecha_cierre1) if self.fecha_cierre1 else '',
            'fecha_cierre2': str(self.fecha_cierre2) if self.fecha_cierre2 else '',
            'asignado_a': self.asignado_a,
            'asignado_nombre': self.asignado.nombre if self.asignado else '',
            'estado': self.estado, 'prioridad': self.prioridad,
            'notas': self.notas or '',
            'created_by': self.created_by,
            'created_at': str(self.created_at)
        }

class Produccion(db.Model):
    __tablename__ = 'produccion'
    id              = db.Column(db.Integer, primary_key=True)
    nombre_programa = db.Column(db.String(250), nullable=False)
    fecha           = db.Column(db.Date)
    elemento        = db.Column(db.Enum('gamma','html','genially','plan_estudio','otro'), default='html')
    modulo_1  = db.Column(db.String(200), default='')
    modulo_2  = db.Column(db.String(200), default='')
    modulo_3  = db.Column(db.String(200), default='')
    modulo_4  = db.Column(db.String(200), default='')
    modulo_5  = db.Column(db.String(200), default='')
    modulo_6  = db.Column(db.String(200), default='')
    modulo_7  = db.Column(db.String(200), default='')
    modulo_8  = db.Column(db.String(200), default='')
    modulo_9  = db.Column(db.String(200), default='')
    modulo_10 = db.Column(db.String(200), default='')
    modulo_11 = db.Column(db.String(200), default='')
    modulo_12 = db.Column(db.String(200), default='')
    asignado_a = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    estado     = db.Column(db.Enum('pendiente','en_progreso','completado'), default='pendiente')
    notas      = db.Column(db.Text)
    created_by = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)

    asignado = db.relationship('Usuario', foreign_keys=[asignado_a])
    creador  = db.relationship('Usuario', foreign_keys=[created_by])

    def to_dict(self):
        d = {
            'id': self.id, 'nombre_programa': self.nombre_programa,
            'fecha': str(self.fecha) if self.fecha else '',
            'elemento': self.elemento, 'estado': self.estado,
            'asignado_a': self.asignado_a,
            'asignado_nombre': self.asignado.nombre if self.asignado else '',
            'notas': self.notas or '',
            'created_by': self.created_by
        }
        for i in range(1, 13):
            d[f'modulo_{i}'] = getattr(self, f'modulo_{i}') or ''
        return d

class Cotizacion(db.Model):
    __tablename__ = 'cotizaciones'
    id           = db.Column(db.Integer, primary_key=True)
    razon_social = db.Column(db.String(200), nullable=False)
    rut          = db.Column(db.String(20), default='')
    direccion    = db.Column(db.String(250), default='')
    giro         = db.Column(db.String(200), default='')
    telefono     = db.Column(db.String(30), default='')
    contacto     = db.Column(db.String(150), default='')
    estado       = db.Column(db.Enum('por_postular','en_postulacion','cerrada','adjudicada','no_adjudicada','desierta'), default='por_postular')
    subtotal     = db.Column(db.Numeric(12,2), default=0)
    iva          = db.Column(db.Numeric(12,2), default=0)
    total        = db.Column(db.Numeric(12,2), default=0)
    notas        = db.Column(db.Text)
    created_by   = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at   = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at   = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)

    items   = db.relationship('CotizacionItem', backref='cotizacion', cascade='all, delete-orphan', lazy=True)
    creador = db.relationship('Usuario', foreign_keys=[created_by])

    def to_dict(self):
        return {
            'id': self.id, 'razon_social': self.razon_social,
            'rut': self.rut or '', 'direccion': self.direccion or '',
            'giro': self.giro or '', 'telefono': self.telefono or '',
            'contacto': self.contacto or '', 'estado': self.estado,
            'subtotal': float(self.subtotal or 0),
            'iva': float(self.iva or 0),
            'total': float(self.total or 0),
            'notas': self.notas or '',
            'items': [it.to_dict() for it in self.items],
            'created_by': self.created_by,
            'created_at': str(self.created_at)
        }

class CotizacionItem(db.Model):
    __tablename__ = 'cotizacion_items'
    id              = db.Column(db.Integer, primary_key=True)
    cotizacion_id   = db.Column(db.Integer, db.ForeignKey('cotizaciones.id'))
    descripcion     = db.Column(db.String(300), nullable=False)
    cantidad        = db.Column(db.Integer, default=1)
    precio_unitario = db.Column(db.Numeric(12,2), default=0)
    total           = db.Column(db.Numeric(12,2), default=0)

    def to_dict(self):
        return {
            'id': self.id, 'descripcion': self.descripcion,
            'cantidad': self.cantidad,
            'precio_unitario': float(self.precio_unitario or 0),
            'total': float(self.total or 0)
        }

class MercadoPublico(db.Model):
    __tablename__ = 'mercado_publico'
    id_postulacion = db.Column(db.String(50), primary_key=True)
    institucion    = db.Column(db.String(200), nullable=False)
    participantes  = db.Column(db.Integer, default=0)
    programa       = db.Column(db.String(250), default='')
    monto          = db.Column(db.Numeric(12,2), default=0)
    estado         = db.Column(db.Enum('por_postular','en_postulacion','cerrada','adjudicada','no_adjudicada','desierta'), default='por_postular')
    notas          = db.Column(db.Text)
    created_by     = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at     = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at     = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)

    def to_dict(self):
        return {
            'id_postulacion': self.id_postulacion, 'institucion': self.institucion,
            'participantes': self.participantes, 'programa': self.programa or '',
            'monto': float(self.monto or 0), 'estado': self.estado,
            'notas': self.notas or '', 'created_by': self.created_by,
            'created_at': str(self.created_at)
        }

class Ejecucion(db.Model):
    __tablename__ = 'ejecucion'
    id               = db.Column(db.Integer, primary_key=True)
    oc               = db.Column(db.String(80), default='')
    institucion      = db.Column(db.String(200), nullable=False)
    fecha_inicio     = db.Column(db.Date)
    participantes    = db.Column(db.Integer, default=0)
    fecha_termino    = db.Column(db.Date)
    factura          = db.Column(db.String(80), default='')
    fecha_facturacion = db.Column(db.Date)
    fecha_envio_informe = db.Column(db.Date)
    cobro1_monto     = db.Column(db.Numeric(12,2), default=0)
    cobro1_fecha     = db.Column(db.Date)
    cobro1_estado    = db.Column(db.Enum('pendiente','cobrado'), default='pendiente')
    cobro2_monto     = db.Column(db.Numeric(12,2), default=0)
    cobro2_fecha     = db.Column(db.Date)
    cobro2_estado    = db.Column(db.Enum('pendiente','cobrado'), default='pendiente')
    seguimiento      = db.Column(db.Text)
    estado           = db.Column(db.Enum('activa','finalizada','cancelada'), default='activa')
    created_by       = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at       = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at       = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id, 'oc': self.oc or '', 'institucion': self.institucion,
            'fecha_inicio': str(self.fecha_inicio) if self.fecha_inicio else '',
            'participantes': self.participantes,
            'fecha_termino': str(self.fecha_termino) if self.fecha_termino else '',
            'factura': self.factura or '',
            'fecha_facturacion': str(self.fecha_facturacion) if self.fecha_facturacion else '',
            'fecha_envio_informe': str(self.fecha_envio_informe) if self.fecha_envio_informe else '',
            'cobro1_monto': float(self.cobro1_monto or 0),
            'cobro1_fecha': str(self.cobro1_fecha) if self.cobro1_fecha else '',
            'cobro1_estado': self.cobro1_estado,
            'cobro2_monto': float(self.cobro2_monto or 0),
            'cobro2_fecha': str(self.cobro2_fecha) if self.cobro2_fecha else '',
            'cobro2_estado': self.cobro2_estado,
            'seguimiento': self.seguimiento or '', 'estado': self.estado,
            'created_by': self.created_by
        }

class Finanza(db.Model):
    __tablename__ = 'finanzas'
    id          = db.Column(db.Integer, primary_key=True)
    tipo        = db.Column(db.Enum('ingreso','gasto'), nullable=False)
    categoria   = db.Column(db.String(120), default='')
    descripcion = db.Column(db.String(300), nullable=False)
    monto       = db.Column(db.Numeric(12,2), nullable=False)
    fecha       = db.Column(db.Date, nullable=False)
    created_by  = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at  = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at  = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id, 'tipo': self.tipo,
            'categoria': self.categoria or '',
            'descripcion': self.descripcion,
            'monto': float(self.monto), 'fecha': str(self.fecha),
            'created_by': self.created_by
        }

class MarketingPub(db.Model):
    __tablename__ = 'marketing_pub'
    id           = db.Column(db.Integer, primary_key=True)
    red_social   = db.Column(db.Enum('facebook','instagram','tiktok','linkedin'), nullable=False)
    titulo       = db.Column(db.String(200), nullable=False)
    descripcion  = db.Column(db.Text)
    fecha        = db.Column(db.Date, nullable=False)
    alcance      = db.Column(db.Integer, default=0)
    interacciones = db.Column(db.Integer, default=0)
    clicks       = db.Column(db.Integer, default=0)
    created_by   = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at   = db.Column(db.DateTime, default=datetime.datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id, 'red_social': self.red_social,
            'titulo': self.titulo, 'descripcion': self.descripcion or '',
            'fecha': str(self.fecha), 'alcance': self.alcance,
            'interacciones': self.interacciones, 'clicks': self.clicks,
            'created_by': self.created_by
        }

class MarketingPlan(db.Model):
    __tablename__ = 'marketing_plan'
    id          = db.Column(db.Integer, primary_key=True)
    red_social  = db.Column(db.Enum('facebook','instagram','tiktok','linkedin'), nullable=False)
    titulo      = db.Column(db.String(200), nullable=False)
    descripcion = db.Column(db.Text)
    fecha       = db.Column(db.Date, nullable=False)
    hora        = db.Column(db.Time)
    completado  = db.Column(db.Boolean, default=False)
    created_by  = db.Column(db.Integer, db.ForeignKey('usuarios.id'))
    created_at  = db.Column(db.DateTime, default=datetime.datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id, 'red_social': self.red_social,
            'titulo': self.titulo, 'descripcion': self.descripcion or '',
            'fecha': str(self.fecha),
            'hora': str(self.hora) if self.hora else '',
            'completado': self.completado, 'created_by': self.created_by
        }


# ── HELPERS ──────────────────────────────────────────────────

def login_required(f):
    @wraps(f)
    def wrapped(*a, **kw):
        if 'user_id' not in session:
            if request.path.startswith('/api/'):
                return jsonify({'error': 'No autenticado'}), 401
            return redirect(url_for('login'))
        return f(*a, **kw)
    return wrapped

def admin_required(f):
    @wraps(f)
    def wrapped(*a, **kw):
        if session.get('rol') not in ('superadmin','admin'):
            return jsonify({'error': 'Sin permisos'}), 403
        return f(*a, **kw)
    return wrapped

def _parse_date(s):
    if not s:
        return None
    try:
        return datetime.date.fromisoformat(s)
    except:
        return None

def _parse_time(s):
    if not s:
        return None
    try:
        return datetime.time.fromisoformat(s)
    except:
        return None

def _j():
    """Get JSON from request"""
    return request.get_json(force=True, silent=True) or {}


# ── AUTH ROUTES ──────────────────────────────────────────────

@app.route('/')
def index():
    if 'user_id' in session:
        return render_template('app.html')
    return redirect(url_for('login'))

@app.route('/login', methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    d = _j() if request.is_json else request.form
    u = Usuario.query.filter_by(username=d.get('username',''), activo=True).first()
    if u and u.password == d.get('password',''):
        session['user_id']  = u.id
        session['username'] = u.username
        session['nombre']   = u.nombre
        session['rol']      = u.rol
        if request.is_json:
            return jsonify({'ok': True, 'user': u.to_dict()})
        return redirect(url_for('index'))
    if request.is_json:
        return jsonify({'error': 'Credenciales inválidas'}), 401
    return render_template('login.html', error='Credenciales inválidas')

@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('login'))

@app.route('/api/me')
@login_required
def api_me():
    u = Usuario.query.get(session['user_id'])
    return jsonify(u.to_dict())

@app.route('/api/users')
@login_required
def api_users():
    return jsonify([u.to_dict() for u in Usuario.query.filter_by(activo=True).all()])


# ── TAREAS ───────────────────────────────────────────────────

@app.route('/api/tareas')
@login_required
def api_tareas_list():
    q = Tarea.query
    if session['rol'] == 'ejecutor':
        q = q.filter_by(asignado_a=session['user_id'])
    return jsonify([t.to_dict() for t in q.order_by(Tarea.created_at.desc()).all()])

@app.route('/api/tareas', methods=['POST'])
@login_required
def api_tareas_create():
    d = _j()
    t = Tarea(
        institucion=d.get('institucion',''),
        n_participantes=int(d.get('n_participantes',0) or 0),
        monto_disponible=float(d.get('monto_disponible',0) or 0),
        monto_ofertado=float(d.get('monto_ofertado',0) or 0),
        fecha_cierre1=_parse_date(d.get('fecha_cierre1')),
        fecha_cierre2=_parse_date(d.get('fecha_cierre2')),
        asignado_a=int(d['asignado_a']) if d.get('asignado_a') else None,
        estado=d.get('estado','pendiente'),
        prioridad=d.get('prioridad','media'),
        notas=d.get('notas',''),
        created_by=session['user_id']
    )
    db.session.add(t)
    db.session.commit()
    return jsonify(t.to_dict()), 201

@app.route('/api/tareas/<int:id>', methods=['PUT'])
@login_required
def api_tareas_update(id):
    t = Tarea.query.get_or_404(id)
    d = _j()
    for k in ['institucion','n_participantes','monto_disponible','monto_ofertado',
              'estado','prioridad','notas']:
        if k in d:
            if k in ('n_participantes',):
                setattr(t, k, int(d[k] or 0))
            elif k in ('monto_disponible','monto_ofertado'):
                setattr(t, k, float(d[k] or 0))
            else:
                setattr(t, k, d[k])
    if 'fecha_cierre1' in d: t.fecha_cierre1 = _parse_date(d['fecha_cierre1'])
    if 'fecha_cierre2' in d: t.fecha_cierre2 = _parse_date(d['fecha_cierre2'])
    if 'asignado_a' in d:    t.asignado_a = int(d['asignado_a']) if d['asignado_a'] else None
    db.session.commit()
    return jsonify(t.to_dict())

@app.route('/api/tareas/<int:id>', methods=['DELETE'])
@login_required
def api_tareas_delete(id):
    t = Tarea.query.get_or_404(id)
    db.session.delete(t)
    db.session.commit()
    return jsonify({'ok': True})


# ── PRODUCCION ───────────────────────────────────────────────

@app.route('/api/produccion')
@login_required
def api_prod_list():
    q = Produccion.query
    if session['rol'] == 'ejecutor':
        q = q.filter_by(asignado_a=session['user_id'])
    return jsonify([p.to_dict() for p in q.order_by(Produccion.created_at.desc()).all()])

@app.route('/api/produccion', methods=['POST'])
@login_required
def api_prod_create():
    d = _j()
    p = Produccion(
        nombre_programa=d.get('nombre_programa',''),
        fecha=_parse_date(d.get('fecha')),
        elemento=d.get('elemento','html'),
        asignado_a=int(d['asignado_a']) if d.get('asignado_a') else None,
        estado=d.get('estado','pendiente'),
        notas=d.get('notas',''),
        created_by=session['user_id']
    )
    for i in range(1,13):
        setattr(p, f'modulo_{i}', d.get(f'modulo_{i}',''))
    db.session.add(p)
    db.session.commit()
    return jsonify(p.to_dict()), 201

@app.route('/api/produccion/<int:id>', methods=['PUT'])
@login_required
def api_prod_update(id):
    p = Produccion.query.get_or_404(id)
    d = _j()
    for k in ['nombre_programa','elemento','estado','notas']:
        if k in d: setattr(p, k, d[k])
    if 'fecha' in d: p.fecha = _parse_date(d['fecha'])
    if 'asignado_a' in d: p.asignado_a = int(d['asignado_a']) if d['asignado_a'] else None
    for i in range(1,13):
        mk = f'modulo_{i}'
        if mk in d: setattr(p, mk, d[mk])
    db.session.commit()
    return jsonify(p.to_dict())

@app.route('/api/produccion/<int:id>', methods=['DELETE'])
@login_required
def api_prod_delete(id):
    p = Produccion.query.get_or_404(id)
    db.session.delete(p)
    db.session.commit()
    return jsonify({'ok': True})


# ── COTIZACIONES ─────────────────────────────────────────────

@app.route('/api/cotizaciones')
@login_required
def api_cot_list():
    return jsonify([c.to_dict() for c in Cotizacion.query.order_by(Cotizacion.created_at.desc()).all()])

@app.route('/api/cotizaciones', methods=['POST'])
@login_required
def api_cot_create():
    d = _j()
    c = Cotizacion(
        razon_social=d.get('razon_social',''),
        rut=d.get('rut',''), direccion=d.get('direccion',''),
        giro=d.get('giro',''), telefono=d.get('telefono',''),
        contacto=d.get('contacto',''), estado=d.get('estado','por_postular'),
        notas=d.get('notas',''), created_by=session['user_id']
    )
    subtotal = 0
    for it in d.get('items', []):
        cant = int(it.get('cantidad',1) or 1)
        pu   = float(it.get('precio_unitario',0) or 0)
        tot  = cant * pu
        subtotal += tot
        c.items.append(CotizacionItem(
            descripcion=it.get('descripcion',''),
            cantidad=cant, precio_unitario=pu, total=tot
        ))
    c.subtotal = subtotal
    c.iva = round(subtotal * 0.19, 2)
    c.total = round(subtotal * 1.19, 2)
    db.session.add(c)
    db.session.commit()
    return jsonify(c.to_dict()), 201

@app.route('/api/cotizaciones/<int:id>', methods=['PUT'])
@login_required
def api_cot_update(id):
    c = Cotizacion.query.get_or_404(id)
    d = _j()
    for k in ['razon_social','rut','direccion','giro','telefono','contacto','estado','notas']:
        if k in d: setattr(c, k, d[k])
    if 'items' in d:
        CotizacionItem.query.filter_by(cotizacion_id=c.id).delete()
        subtotal = 0
        for it in d['items']:
            cant = int(it.get('cantidad',1) or 1)
            pu   = float(it.get('precio_unitario',0) or 0)
            tot  = cant * pu
            subtotal += tot
            c.items.append(CotizacionItem(
                descripcion=it.get('descripcion',''),
                cantidad=cant, precio_unitario=pu, total=tot
            ))
        c.subtotal = subtotal
        c.iva = round(subtotal * 0.19, 2)
        c.total = round(subtotal * 1.19, 2)
    db.session.commit()
    return jsonify(c.to_dict())

@app.route('/api/cotizaciones/<int:id>', methods=['DELETE'])
@login_required
def api_cot_delete(id):
    c = Cotizacion.query.get_or_404(id)
    db.session.delete(c)
    db.session.commit()
    return jsonify({'ok': True})


# ── MERCADO PUBLICO ──────────────────────────────────────────

@app.route('/api/mercado')
@login_required
def api_mercado_list():
    return jsonify([m.to_dict() for m in MercadoPublico.query.order_by(MercadoPublico.created_at.desc()).all()])

@app.route('/api/mercado', methods=['POST'])
@login_required
def api_mercado_create():
    d = _j()
    m = MercadoPublico(
        id_postulacion=d.get('id_postulacion',''),
        institucion=d.get('institucion',''),
        participantes=int(d.get('participantes',0) or 0),
        programa=d.get('programa',''),
        monto=float(d.get('monto',0) or 0),
        estado=d.get('estado','por_postular'),
        notas=d.get('notas',''),
        created_by=session['user_id']
    )
    db.session.add(m)
    db.session.commit()
    return jsonify(m.to_dict()), 201

@app.route('/api/mercado/<id>', methods=['PUT'])
@login_required
def api_mercado_update(id):
    m = MercadoPublico.query.get_or_404(id)
    d = _j()
    for k in ['institucion','programa','estado','notas']:
        if k in d: setattr(m, k, d[k])
    if 'participantes' in d: m.participantes = int(d['participantes'] or 0)
    if 'monto' in d: m.monto = float(d['monto'] or 0)
    db.session.commit()
    return jsonify(m.to_dict())

@app.route('/api/mercado/<id>', methods=['DELETE'])
@login_required
def api_mercado_delete(id):
    m = MercadoPublico.query.get_or_404(id)
    db.session.delete(m)
    db.session.commit()
    return jsonify({'ok': True})


# ── EJECUCION ────────────────────────────────────────────────

@app.route('/api/ejecucion')
@login_required
def api_ejec_list():
    return jsonify([e.to_dict() for e in Ejecucion.query.order_by(Ejecucion.created_at.desc()).all()])

@app.route('/api/ejecucion', methods=['POST'])
@login_required
def api_ejec_create():
    d = _j()
    e = Ejecucion(
        oc=d.get('oc',''), institucion=d.get('institucion',''),
        fecha_inicio=_parse_date(d.get('fecha_inicio')),
        participantes=int(d.get('participantes',0) or 0),
        fecha_termino=_parse_date(d.get('fecha_termino')),
        factura=d.get('factura',''),
        fecha_facturacion=_parse_date(d.get('fecha_facturacion')),
        fecha_envio_informe=_parse_date(d.get('fecha_envio_informe')),
        cobro1_monto=float(d.get('cobro1_monto',0) or 0),
        cobro1_fecha=_parse_date(d.get('cobro1_fecha')),
        cobro1_estado=d.get('cobro1_estado','pendiente'),
        cobro2_monto=float(d.get('cobro2_monto',0) or 0),
        cobro2_fecha=_parse_date(d.get('cobro2_fecha')),
        cobro2_estado=d.get('cobro2_estado','pendiente'),
        seguimiento=d.get('seguimiento',''),
        estado=d.get('estado','activa'),
        created_by=session['user_id']
    )
    db.session.add(e)
    db.session.commit()
    return jsonify(e.to_dict()), 201

@app.route('/api/ejecucion/<int:id>', methods=['PUT'])
@login_required
def api_ejec_update(id):
    e = Ejecucion.query.get_or_404(id)
    d = _j()
    for k in ['oc','institucion','factura','seguimiento','estado',
              'cobro1_estado','cobro2_estado']:
        if k in d: setattr(e, k, d[k])
    for k in ['participantes']:
        if k in d: setattr(e, k, int(d[k] or 0))
    for k in ['cobro1_monto','cobro2_monto']:
        if k in d: setattr(e, k, float(d[k] or 0))
    for k in ['fecha_inicio','fecha_termino','fecha_facturacion',
              'fecha_envio_informe','cobro1_fecha','cobro2_fecha']:
        if k in d: setattr(e, k, _parse_date(d[k]))
    db.session.commit()
    return jsonify(e.to_dict())

@app.route('/api/ejecucion/<int:id>', methods=['DELETE'])
@login_required
def api_ejec_delete(id):
    e = Ejecucion.query.get_or_404(id)
    db.session.delete(e)
    db.session.commit()
    return jsonify({'ok': True})


# ── FINANZAS (solo admins) ───────────────────────────────────

@app.route('/api/finanzas')
@login_required
@admin_required
def api_fin_list():
    return jsonify([f.to_dict() for f in Finanza.query.order_by(Finanza.fecha.desc()).all()])

@app.route('/api/finanzas', methods=['POST'])
@login_required
@admin_required
def api_fin_create():
    d = _j()
    f = Finanza(
        tipo=d.get('tipo','ingreso'),
        categoria=d.get('categoria',''),
        descripcion=d.get('descripcion',''),
        monto=float(d.get('monto',0) or 0),
        fecha=_parse_date(d.get('fecha')) or datetime.date.today(),
        created_by=session['user_id']
    )
    db.session.add(f)
    db.session.commit()
    return jsonify(f.to_dict()), 201

@app.route('/api/finanzas/<int:id>', methods=['PUT'])
@login_required
@admin_required
def api_fin_update(id):
    f = Finanza.query.get_or_404(id)
    d = _j()
    for k in ['tipo','categoria','descripcion']:
        if k in d: setattr(f, k, d[k])
    if 'monto' in d: f.monto = float(d['monto'] or 0)
    if 'fecha' in d: f.fecha = _parse_date(d['fecha']) or f.fecha
    db.session.commit()
    return jsonify(f.to_dict())

@app.route('/api/finanzas/<int:id>', methods=['DELETE'])
@login_required
@admin_required
def api_fin_delete(id):
    f = Finanza.query.get_or_404(id)
    db.session.delete(f)
    db.session.commit()
    return jsonify({'ok': True})

@app.route('/api/finanzas/resumen')
@login_required
@admin_required
def api_fin_resumen():
    from sqlalchemy import func, extract
    rows = db.session.query(
        extract('year', Finanza.fecha).label('anio'),
        extract('month', Finanza.fecha).label('mes'),
        Finanza.tipo,
        func.sum(Finanza.monto).label('total')
    ).group_by('anio','mes', Finanza.tipo).order_by('anio','mes').all()
    data = {}
    for r in rows:
        key = f"{int(r.anio)}-{int(r.mes):02d}"
        if key not in data:
            data[key] = {'mes': key, 'ingresos': 0, 'gastos': 0}
        if r.tipo == 'ingreso':
            data[key]['ingresos'] = float(r.total)
        else:
            data[key]['gastos'] = float(r.total)
    return jsonify(list(data.values()))


# ── MARKETING ────────────────────────────────────────────────

@app.route('/api/marketing/pub')
@login_required
def api_mkt_pub_list():
    return jsonify([p.to_dict() for p in MarketingPub.query.order_by(MarketingPub.fecha.desc()).all()])

@app.route('/api/marketing/pub', methods=['POST'])
@login_required
def api_mkt_pub_create():
    d = _j()
    p = MarketingPub(
        red_social=d.get('red_social','facebook'),
        titulo=d.get('titulo',''),
        descripcion=d.get('descripcion',''),
        fecha=_parse_date(d.get('fecha')) or datetime.date.today(),
        alcance=int(d.get('alcance',0) or 0),
        interacciones=int(d.get('interacciones',0) or 0),
        clicks=int(d.get('clicks',0) or 0),
        created_by=session['user_id']
    )
    db.session.add(p)
    db.session.commit()
    return jsonify(p.to_dict()), 201

@app.route('/api/marketing/pub/<int:id>', methods=['PUT'])
@login_required
def api_mkt_pub_update(id):
    p = MarketingPub.query.get_or_404(id)
    d = _j()
    for k in ['red_social','titulo','descripcion']:
        if k in d: setattr(p, k, d[k])
    if 'fecha' in d: p.fecha = _parse_date(d['fecha']) or p.fecha
    for k in ['alcance','interacciones','clicks']:
        if k in d: setattr(p, k, int(d[k] or 0))
    db.session.commit()
    return jsonify(p.to_dict())

@app.route('/api/marketing/pub/<int:id>', methods=['DELETE'])
@login_required
def api_mkt_pub_delete(id):
    p = MarketingPub.query.get_or_404(id)
    db.session.delete(p)
    db.session.commit()
    return jsonify({'ok': True})

@app.route('/api/marketing/plan')
@login_required
def api_mkt_plan_list():
    return jsonify([p.to_dict() for p in MarketingPlan.query.order_by(MarketingPlan.fecha.asc()).all()])

@app.route('/api/marketing/plan', methods=['POST'])
@login_required
def api_mkt_plan_create():
    d = _j()
    p = MarketingPlan(
        red_social=d.get('red_social','facebook'),
        titulo=d.get('titulo',''),
        descripcion=d.get('descripcion',''),
        fecha=_parse_date(d.get('fecha')) or datetime.date.today(),
        hora=_parse_time(d.get('hora')),
        created_by=session['user_id']
    )
    db.session.add(p)
    db.session.commit()
    return jsonify(p.to_dict()), 201

@app.route('/api/marketing/plan/<int:id>', methods=['DELETE'])
@login_required
def api_mkt_plan_delete(id):
    p = MarketingPlan.query.get_or_404(id)
    db.session.delete(p)
    db.session.commit()
    return jsonify({'ok': True})


# ── DASHBOARD STATS ──────────────────────────────────────────

@app.route('/api/dashboard')
@login_required
def api_dashboard():
    uid = session['user_id']
    rol = session['rol']
    is_admin = rol in ('superadmin','admin')

    tq = Tarea.query
    if not is_admin:
        tq = tq.filter_by(asignado_a=uid)
    tareas_total   = tq.count()
    tareas_pend    = tq.filter_by(estado='pendiente').count()
    tareas_prog    = tq.filter_by(estado='en_progreso').count()
    tareas_comp    = tq.filter_by(estado='completada').count()

    pq = Produccion.query
    if not is_admin:
        pq = pq.filter_by(asignado_a=uid)
    prod_total = pq.count()
    prod_comp  = pq.filter_by(estado='completado').count()

    cot_total = Cotizacion.query.count()
    cot_adj   = Cotizacion.query.filter_by(estado='adjudicada').count()

    ejec_total  = Ejecucion.query.count()
    ejec_activa = Ejecucion.query.filter_by(estado='activa').count()

    return jsonify({
        'tareas': {'total': tareas_total, 'pendientes': tareas_pend,
                   'en_progreso': tareas_prog, 'completadas': tareas_comp},
        'produccion': {'total': prod_total, 'completados': prod_comp},
        'cotizaciones': {'total': cot_total, 'adjudicadas': cot_adj},
        'ejecucion': {'total': ejec_total, 'activas': ejec_activa}
    })


# ── INIT DB (ruta de setup) ─────────────────────────────────

@app.route('/init-db-setup-2026')
def init_db():
    """Crea tablas si no existen y usuarios iniciales"""
    db.create_all()
    if not Usuario.query.filter_by(username='jcadiz').first():
        users = [
            Usuario(username='jcadiz',   password='Camila17$', nombre='José Cádiz',      rol='superadmin'),
            Usuario(username='cdelgado', password='Camila17$', nombre='Camila Delgado',   rol='admin'),
            Usuario(username='croa',     password='Camila17$', nombre='Cristian Roa',     rol='ejecutor'),
            Usuario(username='idelgado', password='Camila17$', nombre='Ignacio Delgado',  rol='ejecutor'),
            Usuario(username='srivera',  password='Camila17$', nombre='Sebastián Rivera', rol='ejecutor'),
        ]
        db.session.add_all(users)
        db.session.commit()
    return '<h2>✅ Base de datos inicializada correctamente</h2><p><a href="/">Ir al login</a></p>'


# ── RUN ──────────────────────────────────────────────────────

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

application = app
