#!/usr/bin/env python3
"""Генератор синтетических прайсов клиник (RU/KZ/EN) + эталонная нормализация + справочник канонов.

Выдаёт:
  fixtures/raw/*            — 6 форматов «сырых» прайсов (Excel/CSV/txt/PNG/PDF)
  fixtures/normalized/golden.json — эталон: каждая строка → канон (оракул для TDD ETL)
  contracts/service_canon.json    — справочник канонических услуг (seed для нормализации)
Детерминированно (seed=42).
"""

import csv
import json
import os
import random
import subprocess
import sys

ROOT = sys.argv[1] if len(sys.argv) > 1 else "."
RAW = os.path.join(ROOT, "fixtures/raw")
NORM = os.path.join(ROOT, "fixtures/normalized")
CONTRACTS = os.path.join(ROOT, "contracts")
for d in (RAW, NORM, CONTRACTS):
    os.makedirs(d, exist_ok=True)
random.seed(42)


def ensure(pkg, mod=None):
    try:
        __import__(mod or pkg)
    except ImportError:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", pkg])


# Справочник канонических медуслуг. lang-тегированные синонимы — материал для нормализации.
CANONS = [
    {
        "id": "cbc",
        "ru": "Общий анализ крови",
        "kz": "Қанның жалпы анализі",
        "en": "Complete blood count (CBC)",
        "category": "Лаборатория",
        "code": "B03.016.003",
        "unit": "1 иссл.",
        "price": (1200, 4500),
        "syn": [
            ("ОАК", "ru"),
            ("Общий анализ крови (развернутый)", "ru"),
            ("Клинический анализ крови", "ru"),
            ("КАК", "ru"),
            ("CBC", "en"),
            ("Қанның жалпы талдауы", "kz"),
        ],
    },
    {
        "id": "uzi_abd",
        "ru": "УЗИ органов брюшной полости",
        "kz": "Құрсақ қуысы ағзаларының УДЗ",
        "en": "Abdominal ultrasound",
        "category": "Диагностика/УЗИ",
        "code": "A04.16.001",
        "unit": "1 иссл.",
        "price": (6000, 13000),
        "syn": [
            ("УЗИ ОБП", "ru"),
            ("УЗИ брюшной полости", "ru"),
            ("Ультразвуковое исследование брюшной полости", "ru"),
            ("Құрсақ қуысы УДЗ", "kz"),
            ("Abdominal US", "en"),
        ],
    },
    {
        "id": "therapist",
        "ru": "Приём врача-терапевта",
        "kz": "Терапевт дәрігердің қабылдауы",
        "en": "General practitioner consultation",
        "category": "Приём специалистов",
        "code": "B01.047.001",
        "unit": "1 приём",
        "price": (4000, 9000),
        "syn": [
            ("Прием терапевта", "ru"),
            ("Консультация терапевта первичная", "ru"),
            ("Терапевт қабылдауы", "kz"),
            ("GP visit", "en"),
        ],
    },
    {
        "id": "ecg",
        "ru": "Электрокардиография (ЭКГ)",
        "kz": "Электрокардиография (ЭКГ)",
        "en": "Electrocardiography (ECG)",
        "category": "Диагностика",
        "code": "A05.10.006",
        "unit": "1 иссл.",
        "price": (2000, 5000),
        "syn": [
            ("ЭКГ", "ru"),
            ("ЭКГ с расшифровкой", "ru"),
            ("Электрокардиография", "ru"),
            ("ECG", "en"),
            ("ЭКГ + описание", "ru"),
        ],
    },
    {
        "id": "glucose",
        "ru": "Глюкоза крови",
        "kz": "Қандағы глюкоза",
        "en": "Blood glucose",
        "category": "Лаборатория",
        "code": "B03.016.006",
        "unit": "1 иссл.",
        "price": (800, 2200),
        "syn": [
            ("Глюкоза", "ru"),
            ("Сахар крови", "ru"),
            ("Қандағы глюкоза", "kz"),
            ("Glucose", "en"),
        ],
    },
    {
        "id": "cardiologist",
        "ru": "Консультация кардиолога",
        "kz": "Кардиолог кеңесі",
        "en": "Cardiologist consultation",
        "category": "Приём специалистов",
        "code": "B01.015.001",
        "unit": "1 приём",
        "price": (5000, 12000),
        "syn": [
            ("Прием кардиолога", "ru"),
            ("Кардиолог консультация первичная", "ru"),
            ("Кардиолог қабылдауы", "kz"),
        ],
    },
    {
        "id": "mri_brain",
        "ru": "МРТ головного мозга",
        "kz": "Бас миының МРТ",
        "en": "Brain MRI",
        "category": "Диагностика/МРТ",
        "code": "A05.23.009",
        "unit": "1 иссл.",
        "price": (18000, 35000),
        "syn": [
            ("МРТ г/м", "ru"),
            ("Магнитно-резонансная томография головного мозга", "ru"),
            ("Бас миы МРТ", "kz"),
            ("Brain MRI", "en"),
        ],
    },
    {
        "id": "biochem",
        "ru": "Биохимический анализ крови",
        "kz": "Қанның биохимиялық анализі",
        "en": "Blood chemistry panel",
        "category": "Лаборатория",
        "code": "B03.016.004",
        "unit": "1 иссл.",
        "price": (3500, 9000),
        "syn": [
            ("Биохимия крови", "ru"),
            ("Б/х крови", "ru"),
            ("Биохимический анализ крови (базовый)", "ru"),
            ("Қан биохимиясы", "kz"),
        ],
    },
    {
        "id": "tooth_ext",
        "ru": "Удаление зуба",
        "kz": "Тісті жұлу",
        "en": "Tooth extraction",
        "category": "Стоматология",
        "code": "A16.07.001",
        "unit": "1 зуб",
        "price": (6000, 15000),
        "syn": [
            ("Удаление зуба простое", "ru"),
            ("Экстракция зуба", "ru"),
            ("Тіс жұлу", "kz"),
            ("Tooth removal", "en"),
        ],
    },
    {
        "id": "xray_chest",
        "ru": "Рентген грудной клетки",
        "kz": "Кеуде клеткасының рентгені",
        "en": "Chest X-ray",
        "category": "Диагностика",
        "code": "A06.09.007",
        "unit": "1 иссл.",
        "price": (2500, 6000),
        "syn": [
            ("Рентгенография ОГК", "ru"),
            ("Флюорография", "ru"),
            ("Кеуде рентгені", "kz"),
            ("Chest X-ray", "en"),
        ],
    },
    {
        "id": "tsh",
        "ru": "Тиреотропный гормон (ТТГ)",
        "kz": "Тиреотропты гормон (ТТГ)",
        "en": "Thyroid-stimulating hormone (TSH)",
        "category": "Лаборатория",
        "code": "B03.016.021",
        "unit": "1 иссл.",
        "price": (1500, 4000),
        "syn": [("ТТГ", "ru"), ("Тиреотропный гормон", "ru"), ("TSH", "en"), ("ТТГ гормоны", "kz")],
    },
    {
        "id": "urinalysis",
        "ru": "Общий анализ мочи",
        "kz": "Зәрдің жалпы анализі",
        "en": "Urinalysis",
        "category": "Лаборатория",
        "code": "B03.016.010",
        "unit": "1 иссл.",
        "price": (900, 2500),
        "syn": [
            ("ОАМ", "ru"),
            ("Общий анализ мочи", "ru"),
            ("Зәр анализі", "kz"),
            ("Urine test", "en"),
        ],
    },
    {
        "id": "gyn",
        "ru": "Приём гинеколога",
        "kz": "Гинеколог қабылдауы",
        "en": "Gynecologist consultation",
        "category": "Приём специалистов",
        "code": "B01.001.001",
        "unit": "1 приём",
        "price": (5000, 11000),
        "syn": [
            ("Прием гинеколога", "ru"),
            ("Консультация гинеколога первичная", "ru"),
            ("Гинеколог кеңесі", "kz"),
        ],
    },
    {
        "id": "covid_pcr",
        "ru": "ПЦР на COVID-19",
        "kz": "COVID-19 ПТР",
        "en": "COVID-19 PCR test",
        "category": "Лаборатория",
        "code": "A26.08.027",
        "unit": "1 иссл.",
        "price": (7000, 16000),
        "syn": [
            ("ПЦР COVID-19", "ru"),
            ("ПЦР-тест на коронавирус", "ru"),
            ("COVID PCR", "en"),
            ("COVID ПТР", "kz"),
        ],
    },
    {
        "id": "vit_d",
        "ru": "Витамин D (25-OH)",
        "kz": "D дәрумені (25-OH)",
        "en": "Vitamin D (25-OH)",
        "category": "Лаборатория",
        "code": "B03.016.115",
        "unit": "1 иссл.",
        "price": (6000, 12000),
        "syn": [
            ("25-OH витамин D", "ru"),
            ("Витамин Д", "ru"),
            ("Vitamin D", "en"),
            ("D витамині", "kz"),
        ],
    },
]
CLINICS = [
    "Клиника «Аль-Шифа»",
    "Медцентр «Сункар»",
    "Diagnostic Lab Astana",
    "Клиника «Береке»",
    "Медцентр «Тенгри»",
]


def price(c):
    return random.randint(c["price"][0] // 100, c["price"][1] // 100) * 100


def variant(c):
    return random.choice([(c["ru"], "ru")] + c["syn"])


GOLDEN = []


def build(file_name, clinic, n):
    rows = []
    for c in random.sample(CANONS, k=min(n, len(CANONS))):
        raw_name, lang = variant(c)
        p = price(c)
        rows.append({"raw_name": raw_name, "price": p, "unit": c["unit"]})
        GOLDEN.append(
            {
                "source_file": file_name,
                "clinic": clinic,
                "raw_name": raw_name,
                "lang": lang,
                "canon_id": c["id"],
                "canonical_ru": c["ru"],
                "canonical_kz": c["kz"],
                "canonical_en": c["en"],
                "category": c["category"],
                "code": c["code"],
                "price": p,
                "currency": "KZT",
                "unit": c["unit"],
            }
        )
    return rows


ensure("openpyxl")
from openpyxl import Workbook

# 1) чистый Excel
rows = build("clinic_alpha_clean.xlsx", CLINICS[0], 9)
wb = Workbook()
ws = wb.active
ws.title = "Прайс"
ws.append(["Наименование услуги", "Цена, тг"])
for r in rows:
    ws.append([r["raw_name"], r["price"]])
wb.save(f"{RAW}/clinic_alpha_clean.xlsx")

# 2) грязный Excel
rows = build("clinic_beta_dirty.xlsx", CLINICS[1], 9)
wb = Workbook()
ws = wb.active
ws.title = "PRICE 2026"
ws.append(["Прайс-лист медцентра «Сункар» (с 01.06.2026)"])
ws.append([])
ws.append(["№", "Услуга / Қызмет", "Стоимость (тенге)", "Ед."])
secs = ["— Лаборатория / Зертхана —", "— Диагностика —", "— Приём специалистов —"]
i = 1
for k, r in enumerate(rows):
    if k % 3 == 0 and secs:
        ws.append([secs.pop(0), None, None, None])
    val = f"от {r['price']}" if random.random() < 0.3 else r["price"]
    ws.append([i, r["raw_name"], val, r["unit"]])
    i += 1
wb.save(f"{RAW}/clinic_beta_dirty.xlsx")

# 3) CSV ;
rows = build("clinic_gamma.csv", CLINICS[2], 8)
with open(f"{RAW}/clinic_gamma.csv", "w", encoding="utf-8", newline="") as f:
    w = csv.writer(f, delimiter=";")
    w.writerow(["service", "price_kzt", "unit"])
    for r in rows:
        w.writerow([r["raw_name"], r["price"], r["unit"]])

# 4) грязный текст
rows = build("clinic_delta_messy.txt", CLINICS[4], 7)
with open(f"{RAW}/clinic_delta_messy.txt", "w", encoding="utf-8") as f:
    f.write("МЕДЦЕНТР «ТЕНГРИ» — действующие цены\n\n")
    for r in rows:
        sep = random.choice([" — ", " ... ", ": ", "  "])
        cur = random.choice(["тг", "тенге", "₸", "KZT"])
        f.write(f"{r['raw_name']}{sep}{r['price']} {cur} ({r['unit']})\n")
    f.write("\n* Цены справочные, уточняйте в регистратуре. Бағалар анықтамалық.\n")

# 5) PNG-скан
rows = build("clinic_epsilon_scan.png", CLINICS[3], 9)
ensure("Pillow", "PIL")
from PIL import Image, ImageDraw, ImageFont


def font(sz):
    for p in [
        "/System/Library/Fonts/Supplemental/Arial.ttf",
        "/Library/Fonts/Arial.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
    ]:
        if os.path.exists(p):
            return ImageFont.truetype(p, sz)
    return ImageFont.load_default()


img = Image.new("RGB", (900, 720), (250, 248, 244))
d = ImageDraw.Draw(img)
d.text((40, 30), "Клиника «Береке» — прайс-лист", fill=(20, 20, 20), font=font(34))
d.line((40, 85, 860, 85), fill=(180, 180, 180), width=2)
y = 110
for r in rows:
    d.text((50, y), r["raw_name"][:42], fill=(30, 30, 30), font=font(23))
    d.text((720, y), f"{r['price']} тг", fill=(30, 30, 30), font=font(23))
    y += 62
img = img.rotate(-1.4, expand=False, fillcolor=(250, 248, 244))
img.save(f"{RAW}/clinic_epsilon_scan.png")

# 6) PDF
rows = build("clinic_zeta_prices.pdf", CLINICS[2], 10)
pdf = "skip"
try:
    ensure("reportlab")
    from reportlab.lib.pagesizes import A4
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfbase.ttfonts import TTFont
    from reportlab.pdfgen import canvas

    fp = next(
        (
            p
            for p in [
                "/Library/Fonts/Arial.ttf",
                "/System/Library/Fonts/Supplemental/Arial.ttf",
                "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
            ]
            if os.path.exists(p)
        ),
        None,
    )
    if fp:
        pdfmetrics.registerFont(TTFont("U", fp))
        c = canvas.Canvas(f"{RAW}/clinic_zeta_prices.pdf", pagesize=A4)
        c.setFont("U", 18)
        c.drawString(60, 790, "Diagnostic Lab Astana — Price list / Прайс")
        c.setFont("U", 12)
        y = 750
        for r in rows:
            c.drawString(60, y, r["raw_name"][:55])
            c.drawRightString(540, y, f"{r['price']} KZT")
            y -= 28
        c.save()
        pdf = "ok"
except Exception as e:
    pdf = f"skip ({e})"

# эталон нормализации + справочник
with open(f"{NORM}/golden.json", "w", encoding="utf-8") as f:
    json.dump(GOLDEN, f, ensure_ascii=False, indent=2)
canon_out = [
    {
        "canon_id": c["id"],
        "canonical_ru": c["ru"],
        "canonical_kz": c["kz"],
        "canonical_en": c["en"],
        "category": c["category"],
        "code": c["code"],
        "unit": c["unit"],
        "synonyms": [{"text": t, "lang": l} for t, l in c["syn"]],
    }
    for c in CANONS
]
with open(f"{CONTRACTS}/service_canon.json", "w", encoding="utf-8") as f:
    json.dump(canon_out, f, ensure_ascii=False, indent=2)

print("PDF:", pdf)
print("raw:", sorted(os.listdir(RAW)))
print("golden rows:", len(GOLDEN), "| canons:", len(CANONS))
