#!/usr/bin/env python3
"""
Обновляет одну запись в documents.db после AI-анализа в activity.php.

Использование:
    python3 update_record.py --id 66999
"""

import argparse
import json
import os
import sqlite3
import sys

import numpy as np
import requests

# ─── Конфиг ───────────────────────────────────────────────────────────────────

BASE_DIR     = os.path.dirname(os.path.abspath(__file__))
DB_PATH      = os.path.join(BASE_DIR, 'baseDoc', 'data', 'documents.db')
WEBHOOK      = 'https://engeocentr.bitrix24.ru/rest/4391/n8rq6nyzr9z8tvdz/'
AITUNNEL_URL = 'https://api.aitunnel.ru/v1/embeddings'
AITUNNEL_KEY = 'sk-aitunnel-0H1gwP4EewVgEnwkMKFE1bjm8ZtzkZ6j'
EMBED_MODEL  = 'gemini-embedding-2-preview'

SELECT = [
    'ID', 'NAME', 'DATE_CREATE', 'CREATED_BY',
    'PROPERTY_1367', 'PROPERTY_423',
    'PROPERTY_389', 'PROPERTY_391', 'PROPERTY_383',
    'PROPERTY_421', 'PROPERTY_401', 'PROPERTY_427',
    'PROPERTY_387', 'PROPERTY_397', 'PROPERTY_413',
    'PROPERTY_415', 'PROPERTY_451', 'PROPERTY_503', 'PROPERTY_2185',
    'PROPERTY_1415', 'PROPERTY_431',
    'PROPERTY_2989',
]

PAYER_MAP  = {'373': 'БДК', '375': 'ТЗК', '377': 'ИНГЕО', '1059': 'ВЦПО'}
STATUS_MAP = {'363': 'Отклонено', '365': 'Согласовано',
              '367': 'Согласовано и распечатано', '419': 'Не установлен'}
TYPE_MAP   = {'357': 'Договор/ДС', '361': 'Счет/Акт'}
OBJECT_MAP = {
    '1071':  'Камергер (общий)',
    '1073':  'Дмитровка (общий)',
    '15881': 'БД-ПА',
    '15883': 'КА-НС',
    '15885': 'КА-ПА',
    '17363': 'Стр.7 Б. Дмитровка, дом.9',
    '39129': 'БД-НС',
}
DOC_PATTERNS = [
    ('КС-2/КС-3',    r'КС-2\s*/\s*КС-3'),
    ('УПД',          r'\bУПД\b'),
    ('Счет-фактура', r'[Сс]чет-фактура|[Сс]ч-ф\b'),
    ('Акт',          r'\bАкт\b'),
    ('Счет',         r'[Сс]чет\s*№'),
    ('КС-2',         r'КС-2\b'),
    ('КС-3',         r'КС-3\b'),
    ('Справка',      r'[Сс]правка'),
    ('Договор',      r'Дог(?:овор)?\.?\s*№'),
    ('ДС',           r'\bДС\s*№'),
]

import re

def scalar(field):
    if not field: return ''
    if isinstance(field, dict):
        vals = list(field.values())
        return vals[0] if vals else ''
    return field

def multi_values(field):
    if not field: return []
    if isinstance(field, dict):
        vals = list(field.values())
        if vals and isinstance(vals[0], list): return vals[0]
        return [str(v) for v in vals if v]
    if isinstance(field, list): return field
    return [str(field)]

def parse_doc_types(text):
    found = []
    for name, pat in DOC_PATTERNS:
        if re.search(pat, text or ''): found.append(name)
    return list(dict.fromkeys(found))

def extract_contract_refs(text):
    refs = []
    for m in re.findall(r'к\s*Договору\s*№\s*([\w\d\.\-\/]+)', text or ''):
        refs.append('Договор №' + m)
    for m in re.findall(r'по\s*ДС\s*№\s*([\w\d\.]+)', text or ''):
        refs.append('ДС №' + m)
    return refs

def strip_html(text):
    if not text: return ''
    return re.sub(r'<[^>]+>', ' ', text).strip()

def get_analysis_text(item):
    res = scalar(item.get('PROPERTY_2989'))
    if isinstance(res, dict): return res.get('TEXT', '')
    return res or ''

def build_record(item: dict) -> dict:
    nom_dat   = scalar(item.get('PROPERTY_391', ''))
    title     = item.get('NAME', '')
    file_ids  = []
    for prop in ['PROPERTY_415', 'PROPERTY_451', 'PROPERTY_503', 'PROPERTY_2185']:
        file_ids += multi_values(item.get(prop))
    file_ids  = [f for f in file_ids if f]
    payer_id  = str(scalar(item.get('PROPERTY_427', '')))
    status_id = str(scalar(item.get('PROPERTY_401', '')))
    type_id   = str(scalar(item.get('PROPERTY_389', '')))
    doc_types = parse_doc_types(nom_dat) or parse_doc_types(title)
    obj_id    = str(scalar(item.get('PROPERTY_387', '')))
    return {
        'element_id':    item['ID'],
        'name':          title,
        'date_create':   item.get('DATE_CREATE', '')[:10],
        'contractor':    scalar(item.get('PROPERTY_1367', '')),
        'company_id':    scalar(item.get('PROPERTY_423', '')),
        'payer':         PAYER_MAP.get(payer_id, payer_id),
        'doc_type_raw':  TYPE_MAP.get(type_id, type_id),
        'doc_types':     doc_types,
        'contract_refs': extract_contract_refs(nom_dat),
        'num_date_str':  nom_dat,
        'num_date_in':   scalar(item.get('PROPERTY_383', '')),
        'amount':        scalar(item.get('PROPERTY_421', '')),
        'status':        STATUS_MAP.get(status_id, status_id),
        'object':        OBJECT_MAP.get(obj_id, scalar(item.get('PROPERTY_387', ''))),
        'expense_item':  scalar(item.get('PROPERTY_397', '')),
        'comment':       scalar(item.get('PROPERTY_413', '')),
        'payment_due':   scalar(item.get('PROPERTY_1415', '')),
        'approved_date': scalar(item.get('PROPERTY_431', '')),
        'file_ids':      file_ids,
        'has_analysis':  bool(get_analysis_text(item)),
        'analysis_text': get_analysis_text(item),
    }

def build_embed_text(record: dict) -> str:
    parts = [
        record.get('name', ''),
        record.get('contractor', ''),
        record.get('payer', ''),
        ' '.join(record.get('doc_types', [])),
        record.get('num_date_str', ''),
        record.get('num_date_in', ''),
        record.get('amount', ''),
        record.get('status', ''),
        record.get('object', ''),
        record.get('expense_item', ''),
        record.get('comment', ''),
        ' '.join(record.get('contract_refs', [])),
        strip_html(record.get('analysis_text', ''))[:2000],
    ]
    return ' '.join(p for p in parts if p).strip()

def fetch_element(element_id: str) -> dict:
    resp = requests.post(WEBHOOK + 'lists.element.get', json={
        'IBLOCK_TYPE_ID': 'bitrix_processes',
        'IBLOCK_ID':      '57',
        'FILTER':         {'ID': element_id},
        'SELECT':         SELECT,
    }, timeout=30)
    resp.raise_for_status()
    items = resp.json().get('result', [])
    if not items:
        raise ValueError(f'Элемент {element_id} не найден в B24')
    return items[0]

def get_embedding(text: str) -> np.ndarray:
    resp = requests.post(AITUNNEL_URL, headers={
        'Authorization': f'Bearer {AITUNNEL_KEY}',
        'Content-Type':  'application/json',
    }, json={'model': EMBED_MODEL, 'input': [text]}, timeout=60)
    resp.raise_for_status()
    return np.array(resp.json()['data'][0]['embedding'], dtype='float32')

def upsert(conn, record: dict, embedding: np.ndarray):
    from datetime import datetime
    embed_text = build_embed_text(record)
    conn.execute("""
        INSERT INTO documents
            (element_id, name, date_create, contractor, company_id, payer, doc_type_raw,
             doc_types, contract_refs, num_date_str, num_date_in, amount, status,
             object, expense_item, comment, payment_due, approved_date,
             file_ids, has_analysis, analysis_text, embed_text,
             embed_model, embedding, indexed_at)
        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        ON CONFLICT(element_id) DO UPDATE SET
            name          = excluded.name,
            date_create   = excluded.date_create,
            contractor    = excluded.contractor,
            company_id    = excluded.company_id,
            payer         = excluded.payer,
            doc_type_raw  = excluded.doc_type_raw,
            doc_types     = excluded.doc_types,
            contract_refs = excluded.contract_refs,
            num_date_str  = excluded.num_date_str,
            num_date_in   = excluded.num_date_in,
            amount        = excluded.amount,
            status        = excluded.status,
            object        = excluded.object,
            expense_item  = excluded.expense_item,
            comment       = excluded.comment,
            payment_due   = excluded.payment_due,
            approved_date = excluded.approved_date,
            file_ids      = excluded.file_ids,
            has_analysis  = excluded.has_analysis,
            analysis_text = excluded.analysis_text,
            embed_text    = excluded.embed_text,
            embed_model   = excluded.embed_model,
            embedding     = excluded.embedding,
            indexed_at    = excluded.indexed_at
    """, (
        record['element_id'], record.get('name', ''), record.get('date_create', ''),
        record.get('contractor', ''), record.get('company_id', ''), record.get('payer', ''),
        record.get('doc_type_raw', ''),
        json.dumps(record.get('doc_types', []),     ensure_ascii=False),
        json.dumps(record.get('contract_refs', []), ensure_ascii=False),
        record.get('num_date_str', ''), record.get('num_date_in', ''),
        record.get('amount', ''), record.get('status', ''),
        record.get('object', ''), record.get('expense_item', ''),
        record.get('comment', ''), record.get('payment_due', ''), record.get('approved_date', ''),
        json.dumps(record.get('file_ids', []), ensure_ascii=False),
        int(record.get('has_analysis', False)),
        record.get('analysis_text', ''),
        embed_text, EMBED_MODEL, embedding.tobytes(),
        datetime.now().isoformat(),
    ))
    conn.commit()

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--id', required=True, help='element_id элемента Битрикс24')
    args = ap.parse_args()

    element_id = str(args.id)
    print(f'Обновляю запись {element_id}...')

    item   = fetch_element(element_id)
    record = build_record(item)
    text   = build_embed_text(record)

    print(f'  Контрагент: {record["contractor"] or "—"}')
    print(f'  Типы: {record["doc_types"]}')
    print(f'  Анализ: {"да" if record["has_analysis"] else "нет"}')
    print(f'  Текст эмбединга: {len(text)} символов')

    vec = get_embedding(text)
    print(f'  Эмбединг: {len(vec)} dim')

    conn = sqlite3.connect(DB_PATH)
    upsert(conn, record, vec)
    conn.close()

    print(f'✅ Готово — запись {element_id} обновлена в {DB_PATH}')

if __name__ == '__main__':
    main()
