#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Importa las VISITAS del sistema legacy SKEEN a skeen.visita (idempotente, dedup por legacy_id).

- Legacy SOLO LECTURA. Crudo guardado en extraccion/visitas_legacy_YYYYMMDD_HHMMSS.json.
- date_start: created_at interpretado en America/Tijuana (convertido a UTC para que
  los filtros del frontend por fecha caigan en el día correcto).
- doctor_id: matching por tokens (sin acentos) de medico_principal contra hr.employee.
- cosmetologa_id: mapa fijo de ids legacy conocidos ({79: XIMENA, 80: ELY}); otros → False
  (el legacy no expone endpoint de catálogo — /medicos/json no existe).
- state = 'completada' para todas (los status legacy son del flujo viejo y no mapean limpio).

Uso: /root/odoo-venv/bin/python /root/migracion/importar_visitas.py
"""
import html
import json
import sys
import time
import unicodedata
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo

sys.path.insert(0, '/root/migracion')
sys.path.insert(0, '/root/skeen-odoo')

import extraer_skeen as ex  # login(), fetch_json()

import odoo
from odoo import api, SUPERUSER_ID

TZ_LOCAL = ZoneInfo('America/Tijuana')
STAMP = datetime.now().strftime('%Y%m%d_%H%M%S')
RAW_PATH = Path(f'/root/migracion/extraccion/visitas_legacy_{STAMP}.json')

# Cosmetólogas conocidas del legacy (id → nombre en hr.employee)
COSMETOLOGAS_LEGACY = {79: 'XIMENA', 80: 'ELY'}


def log(msg):
    print(f'[{datetime.now().strftime("%H:%M:%S")}] {msg}', flush=True)


def strip_accents(s):
    return ''.join(c for c in unicodedata.normalize('NFD', s or '') if unicodedata.category(c) != 'Mn')


def norm(s):
    return ' '.join(strip_accents(str(s or '')).lower().split())


def visitas_params(start, length):
    """Params DataTables completos (el endpoint da 500 si faltan)"""
    p = {
        'draw': 1, 'start': start, 'length': length,
        'search[value]': '', 'search[regex]': 'false',
        'fecha_registro': '', 'medicos_id': '', 'cosmetologos_id': '',
        'order[0][column]': '0', 'order[0][dir]': 'desc',
        '_': int(time.time() * 1000),
    }
    for i in range(10):
        p[f'columns[{i}][data]'] = str(i)
        p[f'columns[{i}][searchable]'] = 'true'
        p[f'columns[{i}][orderable]'] = 'true'
        p[f'columns[{i}][search][value]'] = ''
        p[f'columns[{i}][search][regex]'] = 'false'
    return p


def main():
    # ---------- Odoo ----------
    odoo.tools.config.parse_config(['-c', '/root/skeen-odoo/odoo.conf'])
    db = odoo.sql_db.db_connect('skeen_odoo')
    cr = db.cursor()
    env = api.Environment(cr, SUPERUSER_ID, {})
    Visita = env['skeen.visita'].sudo()

    log('Cargando cachés de Odoo...')
    partner_by_legacy = {}
    cr.execute("SELECT id, legacy_id FROM res_partner WHERE legacy_id IS NOT NULL AND legacy_id != ''")
    for pid, lid in cr.fetchall():
        partner_by_legacy[str(lid)] = pid

    existing_visitas = set()
    cr.execute("SELECT legacy_id FROM skeen_visita WHERE legacy_id IS NOT NULL AND legacy_id != ''")
    existing_visitas = {r[0] for r in cr.fetchall()}

    service_by_name = {}
    cr.execute("SELECT id, name FROM skeen_servicio")
    for sid, name in cr.fetchall():
        if name:
            service_by_name.setdefault(norm(name), sid)

    employees = []
    cr.execute("SELECT id, name FROM hr_employee")
    for eid, name in cr.fetchall():
        employees.append((eid, name or ''))
    # tokens significativos por empleado (sin dr/dra ni tokens cortos)
    emp_tokens = []
    for eid, name in employees:
        toks = [t for t in norm(name).split() if len(t) > 3 and t not in ('dra.', 'dr.')]
        if toks:
            emp_tokens.append((eid, name, set(toks)))
    log(f'  Partners con legacy_id: {len(partner_by_legacy)} | Visitas ya importadas: {len(existing_visitas)} | Empleados: {len(employees)}')

    def match_doctor(texto):
        t = norm(texto)
        if not t:
            return False
        best = False
        for eid, name, toks in emp_tokens:
            if toks and all(tok in t for tok in toks):
                # preferir el match con más tokens (más específico)
                if not best or len(toks) > best[1]:
                    best = (eid, len(toks))
        return best[0] if best else False

    employee_by_name = {norm(name): eid for eid, name in employees}
    cosmetologa_map = {}
    for cid, nombre in COSMETOLOGAS_LEGACY.items():
        eid = employee_by_name.get(norm(nombre))
        if eid:
            cosmetologa_map[cid] = eid
    log(f'  Cosmetólogas mapeadas: {cosmetologa_map}')

    # ---------- Legacy ----------
    ex.login()
    log('Descargando visitas del legacy...')
    rows = []
    start = 0
    page_size = 500
    while True:
        data = ex.fetch_json('/visitas/json', visitas_params(start, page_size))
        chunk = data.get('data', [])
        if not chunk:
            break
        rows.extend(chunk)
        total = data.get('recordsTotal') or 0
        log(f'  página start={start}: {len(chunk)} (acumulado {len(rows)}/{total})')
        if len(chunk) < page_size:
            break
        start += page_size
        time.sleep(0.3)
    log(f'Total visitas legacy: {len(rows)}')

    with open(RAW_PATH, 'w', encoding='utf-8') as f:
        json.dump(rows, f, ensure_ascii=False)
    log(f'Crudo guardado en {RAW_PATH}')

    # ---------- Importación ----------
    importadas = 0
    omitidas_sin_paciente = 0
    omitidas_existentes = 0
    errores = 0
    batch = []
    for r in rows:
        legacy_id = str(r.get('id') or '').strip()
        if not legacy_id:
            continue
        if legacy_id in existing_visitas:
            omitidas_existentes += 1
            continue

        exp_id = str(r.get('expedientes_id') or '').strip()
        partner_id = partner_by_legacy.get(exp_id)
        if not partner_id:
            omitidas_sin_paciente += 1
            continue

        # created_at legacy es hora local Tijuana → UTC
        created = (r.get('created_at') or '').strip()
        try:
            dt_local = datetime.strptime(created, '%Y-%m-%d %H:%M:%S').replace(tzinfo=TZ_LOCAL)
            date_start = dt_local.astimezone(ZoneInfo('UTC')).strftime('%Y-%m-%d %H:%M:%S')
        except Exception:
            date_start = created or False

        motivo = html.unescape(r.get('otro_motivo') or '').strip()
        conceptos = r.get('conceptos') or []
        partes = []
        for c in conceptos:
            titulo = html.unescape(c.get('titulo') or '').strip()
            subtotal = c.get('subtotal') or ''
            partes.append(f'{titulo} — ${subtotal}' if subtotal else titulo)
        notas = f"Conceptos: {'; '.join(partes)}" if partes else ''

        cosmetologa_id = cosmetologa_map.get(r.get('cosmetologos_id') or 0, False)

        # Servicio: match exacto normalizado del primer concepto (no crea servicios)
        servicio_id = False
        if conceptos:
            servicio_id = service_by_name.get(norm(html.unescape(conceptos[0].get('titulo') or '')), False)

        batch.append({
            'legacy_id': legacy_id,
            'partner_id': partner_id,
            'doctor_id': match_doctor(r.get('medico_principal')),
            'cosmetologa_id': cosmetologa_id,
            'servicio_id': servicio_id,
            'date_start': date_start,
            'motivo': motivo,
            'notas': notas,
            'state': 'completada',
        })
        existing_visitas.add(legacy_id)  # dedup dentro del mismo lote

        if len(batch) >= 500:
            try:
                Visita.create(batch)
                importadas += len(batch)
                batch = []
                cr.commit()
                log(f'  {importadas} visitas importadas...')
            except Exception as e:
                cr.rollback()
                log(f'  ERROR en lote: {e}')
                errores += len(batch)
                batch = []
    if batch:
        try:
            Visita.create(batch)
            importadas += len(batch)
            cr.commit()
        except Exception as e:
            cr.rollback()
            log(f'  ERROR en lote final: {e}')
            errores += len(batch)

    cr.close()
    log('========== RESUMEN VISITAS ==========')
    log(f'Importadas:              {importadas}')
    log(f'Ya existían (legacy_id): {omitidas_existentes}')
    log(f'Omitidas sin paciente:   {omitidas_sin_paciente}')
    log(f'Errores:                 {errores}')


if __name__ == '__main__':
    main()
