#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Importador rápido de servicios, citas, ventas y monederos SKEEN a Odoo."""
import csv
import sys
import re
import html
import hashlib
from pathlib import Path

ODOO_PATH = Path('/root/skeen-odoo')
sys.path.insert(0, str(ODOO_PATH))

import odoo
from odoo import api, SUPERUSER_ID

DATA_DIR = Path('/root/migracion/datos')


def normalize_bool(v):
    return str(v).lower() in ('1', 'true', 'si', 'sí')


def parse_time(value):
    if not value:
        return 9.0
    value = str(value).strip()
    if ':' in value:
        h, m = value.split(':')[:2]
        return float(h) + float(m) / 60.0
    try:
        return float(value)
    except ValueError:
        return 9.0


def extract_service(text, patient_name):
    """Extrae el nombre del servicio del campo servicio_nombre."""
    text = html.unescape(text or '')
    if patient_name:
        text = re.sub(r'^' + re.escape(patient_name) + r'\s*[-–—]\s*', '', text, flags=re.IGNORECASE)
    text = re.sub(r'\s*\(\d+\s*(sesión|sesiones|unidad|unidades)\s*\)\s*$', '', text, flags=re.IGNORECASE)
    text = ' '.join(text.split())
    return text.strip()


def normalize_name(name):
    return ' '.join(str(name).split()).lower()


def main():
    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, {})

    Partner = env['res.partner'].sudo()
    Servicio = env['skeen.servicio'].sudo()
    Employee = env['hr.employee'].sudo()
    CitaModel = env.registry['skeen.cita']
    Cita = env['skeen.cita'].sudo()
    Venta = env['skeen.venta'].sudo()
    Monedero = env['skeen.monedero'].sudo()
    MonederoTx = env['skeen.monedero.transaction'].sudo()

    # Cachés en memoria (SQL directo para velocidad)
    print('Cargando cachés...')
    partner_by_phone = {}
    partner_by_name = {}
    partner_by_legacy = {}
    cr.execute("SELECT id, phone, name, legacy_id FROM res_partner WHERE is_patient = true")
    for pid, phone, name, legacy_id in cr.fetchall():
        if phone:
            partner_by_phone.setdefault(phone, pid)
        if name:
            partner_by_name.setdefault(normalize_name(name), pid)
        if legacy_id:
            partner_by_legacy[legacy_id] = pid

    service_by_code = {}
    service_by_name = {}
    cr.execute("SELECT id, code, name FROM skeen_servicio")
    for sid, code, name in cr.fetchall():
        if code:
            service_by_code[code] = sid
        if name:
            service_by_name[normalize_name(name)] = sid

    doctor_by_name = {}
    cr.execute("SELECT id, name FROM hr_employee")
    for eid, name in cr.fetchall():
        if name:
            doctor_by_name[normalize_name(name)] = eid
    print(f'  Pacientes: {len(partner_by_phone)} por teléfono, {len(partner_by_name)} por nombre')
    print(f'  Servicios: {len(service_by_name)}')
    print(f'  Doctores: {len(doctor_by_name)}')

    skipped_reasons = {'no_patient': 0, 'no_service': 0, 'no_date': 0}

    def resolve_partner(row, phone_field='paciente_telefono', name_field='paciente_nombre', legacy_field='expedientes_id'):
        legacy_id = (row.get(legacy_field) or '').strip()
        if legacy_id and legacy_id in partner_by_legacy:
            return partner_by_legacy[legacy_id]
        phone = (row.get(phone_field) or '').strip()
        if phone and phone in partner_by_phone:
            return partner_by_phone[phone]
        name = normalize_name(row.get(name_field, ''))
        if name and name in partner_by_name:
            return partner_by_name[name]
        return False

    # Desactivar validación de solapamiento de citas
    original_check = CitaModel._check_availability
    CitaModel._check_availability = lambda self: None

    resultados = {}

    # 1. Servicios
    print('Importando servicios...')
    category_map = {
        'consulta': 'consulta',
        'tratamiento': 'tratamiento',
        'procedimiento': 'procedimiento',
        'paquete': 'paquete',
        'servicios_medicos': 'consulta',
        'procedimientos_cosmeticos': 'procedimiento',
        'tecnologia_medica': 'tratamiento',
        'cosmetologia_basica': 'tratamiento',
        'cosmetologia_avanzada': 'tratamiento',
    }
    seen_codes = set()
    batch = []
    count = 0
    with open(DATA_DIR / 'servicios.csv', newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            code = (row.get('codigo') or '').strip()
            name = (row.get('nombre') or '').strip()
            if not code or not name or code in seen_codes:
                continue
            seen_codes.add(code)
            if code in service_by_code:
                continue
            cat = category_map.get((row.get('categoria') or '').strip().lower(), 'tratamiento')
            batch.append({
                'name': name,
                'code': code,
                'category': cat,
                'price': float(row.get('precio', 0) or 0),
                'duration_min': int(row.get('duracion_min', 30) or 30),
                'description': row.get('descripcion', ''),
                'color': row.get('color', '#1abc9c'),
                'service_group': row.get('grupo', ''),
                'is_favorite': normalize_bool(row.get('favorito')),
                'active': normalize_bool(row.get('activo', '1')),
            })
            if len(batch) >= 100:
                created = Servicio.create(batch)
                count += len(created)
                cr.commit()
                batch = []
    if batch:
        created = Servicio.create(batch)
        count += len(created)
        cr.commit()
    # Recargar caché de servicios (SQL directo)
    service_by_code = {}
    service_by_name = {}
    cr.execute("SELECT id, code, name FROM skeen_servicio")
    for sid, code, name in cr.fetchall():
        if code:
            service_by_code[code] = sid
        if name:
            service_by_name[normalize_name(name)] = sid
    resultados['servicios'] = count
    print(f'  {count} servicios creados')

    # Helper: obtener o crear servicio por nombre
    service_name_cache = dict(service_by_name)

    def get_or_create_service_by_name(name):
        name = name.strip()
        if not name:
            name = 'Servicio genérico'
        key = normalize_name(name)
        if key in service_name_cache:
            return service_name_cache[key]
        # Crear nuevo servicio
        code_hash = hashlib.md5(key.encode('utf-8')).hexdigest()[:12]
        code = f'HIST_{code_hash}'
        new_s = Servicio.create({
            'name': name,
            'code': code,
            'category': 'tratamiento',
            'price': 0,
            'duration_min': 30,
        })
        service_name_cache[key] = new_s.id
        return new_s.id

    # 2. Citas
    print('Importando citas...')
    state_map = {
        'pendiente': 'pending',
        'confirmada': 'confirmed',
        'llego': 'arrived',
        'en_progreso': 'in_progress',
        'completada': 'done',
        'cancelada': 'cancelled',
        'no_show': 'no_show',
    }
    payment_state_map = {
        'no_pagado': 'not_paid',
        'parcial': 'partial',
        'pagado': 'paid',
    }
    batch = []
    count = 0
    skipped = 0
    BATCH_SIZE = 500
    with open(DATA_DIR / 'citas.csv', newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            partner_id = resolve_partner(row)
            date_str = (row.get('fecha') or '').strip()
            if not partner_id:
                skipped += 1
                skipped_reasons['no_patient'] += 1
                continue
            if not date_str:
                skipped += 1
                skipped_reasons['no_date'] += 1
                continue

            service_name = extract_service(row.get('servicio_nombre', ''), row.get('paciente_nombre', ''))
            if not service_name:
                service_name = row.get('servicio_nombre', '').strip() or 'Servicio genérico'
            servicio_id = get_or_create_service_by_name(service_name)

            doctor_name = (row.get('doctor_nombre') or '').strip()
            doctor_id = doctor_by_name.get(normalize_name(doctor_name)) if doctor_name else False

            branch = 'rosarito'
            if (row.get('sucursal') or '').strip().lower() in ('tijuana', 'tj'):
                branch = 'tijuana'

            medium = 'onsite'
            if (row.get('medio') or '').strip().lower() in ('videollamada', 'video', 'remota'):
                medium = 'videocall'

            state = state_map.get((row.get('estado') or '').strip().lower(), 'confirmed')
            payment_state = payment_state_map.get((row.get('estado_pago') or '').strip().lower(), 'not_paid')

            batch.append({
                'partner_id': partner_id,
                'servicio_id': servicio_id,
                'date': date_str,
                'time': parse_time(row.get('hora')),
                'state': state,
                'payment_state': payment_state,
                'amount_paid': float(row.get('monto_pagado', 0) or 0),
                'doctor_id': doctor_id,
                'branch': branch,
                'medium': medium,
                'notes': row.get('notas', ''),
            })

            if len(batch) >= BATCH_SIZE:
                Cita.create(batch)
                count += len(batch)
                batch = []
                cr.commit()
                print(f'  {count} citas creadas')
    if batch:
        Cita.create(batch)
        count += len(batch)
        cr.commit()
    resultados['citas'] = count
    print(f'  {count} citas creadas, {skipped} omitidas ({skipped_reasons})')

    # 3. Ventas
    print('Importando ventas...')
    state_map_venta = {
        'borrador': 'draft',
        'confirmada': 'confirmed',
        'pagada': 'paid',
        'parcial': 'partial',
        'cancelada': 'cancelled',
    }
    count = 0
    skipped = 0
    batch = []
    BATCH_SIZE = 500
    with open(DATA_DIR / 'ventas.csv', newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            partner_id = resolve_partner(row)
            if not partner_id:
                skipped += 1
                skipped_reasons['no_patient'] += 1
                continue

            service_name = extract_service(row.get('servicio_nombre', ''), row.get('paciente_nombre', ''))
            if not service_name:
                service_name = row.get('servicio_nombre', '').strip() or 'Servicio genérico'
            servicio_id = get_or_create_service_by_name(service_name)

            qty = float(row.get('cantidad', 1) or 1)
            price = float(row.get('precio_unitario', 0) or 0)
            discount = float(row.get('descuento', 0) or 0)
            tax = float(row.get('impuesto', 0) or 0)
            state = state_map_venta.get((row.get('estado') or '').strip().lower(), 'confirmed')
            date = row.get('fecha') or odoo.fields.Date.today()

            batch.append({
                'partner_id': partner_id,
                'date': date,
                'discount': discount,
                'tax': tax,
                'state': state,
                'notes': row.get('notas', ''),
                'line_ids': [(0, 0, {
                    'service_id': servicio_id,
                    'description': service_name or 'Servicio',
                    'quantity': qty,
                    'price_unit': price,
                })],
            })

            if len(batch) >= BATCH_SIZE:
                Venta.create(batch)
                count += len(batch)
                batch = []
                cr.commit()
                print(f'  {count} ventas creadas')
    if batch:
        Venta.create(batch)
        count += len(batch)
        cr.commit()
    resultados['ventas'] = count
    print(f'  {count} ventas creadas, {skipped} omitidas')

    # 4. Monederos
    print('Importando monederos...')
    count = 0
    skipped = 0
    batch_monedero = []
    batch_tx = []
    BATCH_SIZE = 500
    with open(DATA_DIR / 'monederos.csv', newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            phone = (row.get('telefono') or '').strip()
            points = int(float(row.get('puntos', 0) or 0))
            if not phone or phone not in partner_by_phone:
                skipped += 1
                continue
            partner_id = partner_by_phone[phone]
            existing = Monedero.search([('partner_id', '=', partner_id)], limit=1)
            if existing:
                existing.write({'points': points})
                count += 1
                continue

            batch_monedero.append({
                'partner_id': partner_id,
                'points': points,
            })

            if len(batch_monedero) >= BATCH_SIZE:
                created = Monedero.create(batch_monedero)
                for m in created:
                    batch_tx.append({
                        'monedero_id': m.id,
                        'type': 'adjustment',
                        'points': m.points,
                        'description': 'Saldo inicial migrado de SKEEN',
                        'reference': 'MIGRACION',
                    })
                if batch_tx:
                    MonederoTx.create(batch_tx)
                    batch_tx = []
                count += len(batch_monedero)
                batch_monedero = []
                cr.commit()
                print(f'  {count} monederos creados')
    if batch_monedero:
        created = Monedero.create(batch_monedero)
        for m in created:
            batch_tx.append({
                'monedero_id': m.id,
                'type': 'adjustment',
                'points': m.points,
                'description': 'Saldo inicial migrado de SKEEN',
                'reference': 'MIGRACION',
            })
        if batch_tx:
            MonederoTx.create(batch_tx)
        count += len(batch_monedero)
        cr.commit()
    resultados['monederos'] = count
    print(f'  {count} monederos creados, {skipped} omitidos')

    # Restaurar validación
    CitaModel._check_availability = original_check
    cr.commit()
    cr.close()

    print('Importación completada:')
    for k, v in resultados.items():
        print(f'  {k}: {v} registros')


if __name__ == '__main__':
    main()
