# -*- coding: utf-8 -*-
"""Generate TTRPG-License-Key-Instructions.docx (run: python scripts/generate-license-key-docx.py)."""
from __future__ import annotations

from pathlib import Path

from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Pt

ROOT = Path(__file__).resolve().parents[1]
OUT_PATHS = [
    ROOT / "docs" / "TTRPG-License-Key-Instructions.docx",
    Path(r"D:\TTRPG-Release\TTRPG-License-Key-Instructions.docx"),
]


def add_bullet(doc: Document, text: str, level: int = 0) -> None:
    style = "List Bullet" if level == 0 else "List Bullet 2"
    doc.add_paragraph(text, style=style)


def add_step(doc: Document, n: int, title: str, body: str) -> None:
    p = doc.add_paragraph()
    p.add_run(f"Шаг {n}. {title}. ").bold = True
    p.add_run(body)


def add_code(doc: Document, text: str) -> None:
    for line in text.strip().splitlines():
        p = doc.add_paragraph(line)
        p.style = "No Spacing"
        for run in p.runs:
            run.font.name = "Consolas"
            run.font.size = Pt(9)


def build_document() -> Document:
    doc = Document()
    normal = doc.styles["Normal"]
    normal.font.name = "Calibri"
    normal.font.size = Pt(11)

    title = doc.add_heading("TTRPG Player — выдача нового лицензионного ключа", level=0)
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER

    doc.add_paragraph(
        "Инструкция для администратора: добавление нового продуктового ключа в базу "
        "сервера лицензий. Пользователь вводит этот ключ в приложении; сервер выдаёт "
        "подписанный лицензионный токен при активации."
    )

    doc.add_heading("Термины", level=1)
    doc_terms = [
        (
            "Продуктовый ключ",
            "строка вида TTRPG-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX — выдаётся покупателю, "
            "хранится в data.json на сервере.",
        ),
        (
            "Лицензионный токен",
            "выдаётся автоматически при активации в приложении; вручную создавать не нужно.",
        ),
        (
            "sub",
            "внутренний идентификатор лицензии на сервере (отзыв, учёт устройств).",
        ),
    ]
    for term, desc in doc_terms:
        p = doc.add_paragraph()
        p.add_run(f"{term} — ").bold = True
        p.add_run(desc)

    doc.add_heading("Где лежит сервер (продакшен)", level=1)
    srv = doc.add_table(rows=6, cols=2)
    srv.style = "Table Grid"
    srv.rows[0].cells[0].text = "Параметр"
    srv.rows[0].cells[1].text = "Значение"
    server_rows = [
        ("Домен", "https://license.ttrpgplayer.ru/"),
        ("VPS", "185.173.94.234"),
        ("Папка проекта", "/var/www/license_mailib_ru"),
        ("Файл ключей", "/var/www/license_mailib_ru/data.json"),
        ("Пользователь Linux", "dndlicense"),
    ]
    for i, (k, v) in enumerate(server_rows, start=1):
        srv.rows[i].cells[0].text = k
        srv.rows[i].cells[1].text = v

    doc.add_heading("Редактирование data.json (удобный редактор)", level=1)
    doc.add_paragraph(
        "Не используйте nano, если нужны выделение мышью и привычное копирование. "
        "Рекомендуется Cursor или VS Code с расширением Remote - SSH."
    )
    add_step(
        doc,
        1,
        "Настроить SSH",
        "В %USERPROFILE%\\.ssh\\config добавьте хост (ключ ttrpg_updates_root — тот же, "
        "что для публикации обновлений):",
    )
    add_code(
        doc,
        """Host mailib-vps
    HostName 185.173.94.234
    User root
    IdentityFile C:\\Users\\Administrator\\.ssh\\ttrpg_updates_root""",
    )
    add_step(
        doc,
        2,
        "Подключиться",
        "F1 → Remote-SSH: Connect to Host… → mailib-vps.",
    )
    add_step(
        doc,
        3,
        "Открыть папку",
        "File → Open Folder → /var/www/license_mailib_ru → открыть data.json.",
    )
    add_bullet(doc, "Альтернатива: WinSCP (SFTP) → правый клик по data.json → Edit.")
    add_bullet(
        doc,
        "Альтернатива: scp скачать файл на ПК, править в Cursor, scp залить обратно.",
    )

    doc.add_heading("Выдача нового ключа", level=1)

    add_step(
        doc,
        1,
        "Резервная копия",
        "На сервере (SSH или терминал в Remote SSH):",
    )
    add_code(
        doc,
        "cd /var/www/license_mailib_ru\n"
        "cp data.json \"data.json.bak.$(date +%F-%H%M%S)\"",
    )

    add_step(
        doc,
        2,
        "Сгенерировать запись (Node.js на сервере)",
        "Выполнить в каталоге /var/www/license_mailib_ru. Скрипт добавит запись в "
        "productKeys и выведет новый ключ в консоль. Срок и лимит устройств — в начале скрипта.",
    )
    add_code(
        doc,
        r"""node -e "
const fs = require('node:fs');
const crypto = require('node:crypto');

const dataPath = process.env.DND_LICENSE_DATA_PATH || './data.json';
const data = JSON.parse(fs.readFileSync(dataPath, 'utf8'));

const maxDevices = 3;
const expiresAtSec = Math.floor(new Date('2027-12-31T23:59:59Z').getTime() / 1000);

const key = 'TTRPG-' + crypto.randomUUID().toUpperCase();
const sub = 'lic_' + crypto.randomUUID().replace(/-/g, '');

data.productKeys ??= [];
data.productKeys.push({
  key,
  sub,
  pid: 'dnd_player',
  maxDevices,
  expiresAtSec
});

fs.writeFileSync(dataPath, JSON.stringify(data, null, 2) + '\n');
console.log('NEW PRODUCT KEY:', key);
console.log('sub:', sub);
console.log('expiresAtSec:', expiresAtSec);
console.log('maxDevices:', maxDevices);
"
""",
    )
    doc.add_paragraph(
        "Сохраните выведенный NEW PRODUCT KEY — его передаёте пользователю. "
        "Формат ключа должен соответствовать шаблону TTRPG-… (заглавные hex-символы)."
    )

    add_step(
        doc,
        3,
        "Или добавить вручную в data.json",
        "В массив productKeys добавьте объект (пример):",
    )
    add_code(
        doc,
        """{
  "key": "TTRPG-12345678-1234-1234-1234-123456789ABC",
  "sub": "lic_unique_id",
  "pid": "dnd_player",
  "maxDevices": 3,
  "expiresAtSec": 1830268799
}""",
    )
    fields = doc.add_table(rows=6, cols=2)
    fields.style = "Table Grid"
    fields.rows[0].cells[0].text = "Поле"
    fields.rows[0].cells[1].text = "Описание"
    field_rows = [
        ("key", "Продуктовый ключ для пользователя (уникальный)"),
        ("sub", "Уникальный ID лицензии на сервере"),
        ("pid", "Обычно dnd_player"),
        ("maxDevices", "Сколько разных deviceId можно активировать"),
        ("expiresAtSec", "Срок действия (Unix time, секунды)"),
    ]
    for i, (k, v) in enumerate(field_rows, start=1):
        fields.rows[i].cells[0].text = k
        fields.rows[i].cells[1].text = v

    add_step(
        doc,
        4,
        "Права на файл (если правили от root)",
        "chown dndlicense:dndlicense /var/www/license_mailib_ru/data.json",
    )

    add_step(
        doc,
        5,
        "Перезапуск сервиса",
        "После изменения data.json:",
    )
    add_code(doc, "sudo -u dndlicense pm2 restart dnd-license")

    doc.add_heading("Проверка", level=1)
    add_bullet(doc, "Сервер жив: curl https://license.ttrpgplayer.ru/health → {\"ok\":true}")
    add_bullet(
        doc,
        "В приложении TTRPG Player: «Указать ключ» → вставить продуктовый ключ → активация.",
    )
    add_bullet(
        doc,
        "При ошибке unknown_product_key — ключ не в data.json или опечатка; "
        "too_many_devices — превышен maxDevices.",
    )

    doc.add_heading("Отзыв лицензии", level=1)
    doc.add_paragraph(
        "Чтобы отозвать лицензию по sub, добавьте sub в массив revokedSubs в data.json "
        "и перезапустите pm2. Либо POST /v1/admin/revoke с Bearer-токеном администратора "
        "(токен хранится только на сервере, в эту инструкцию не включён)."
    )

    doc.add_heading("Локальная разработка", level=1)
    add_bullet(
        doc,
        "Исходники сервера на ПК: D:\\Work\\my_projects\\dnd_project\\DndGamePlayerLicenseServer",
    )
    add_bullet(doc, "Локальный data.json: скопировать из data.example.json")
    add_bullet(doc, "Запуск: npm start (нужен LICENSE_PRIVATE_KEY_PEM)")
    add_bullet(doc, "Порт по умолчанию: 3847")

    doc.add_heading("Частые проблемы", level=1)
    problems = [
        (
            "Ключ не принимается в приложении",
            "Проверьте формат TTRPG-… (8-4-4-4-12 hex), что запись есть в productKeys, "
            "сервер перезапущен после правки data.json.",
        ),
        (
            "JSON сломан после правки",
            "Восстановите из data.json.bak.*; проверьте запятые и кавычки.",
        ),
        (
            "Нет прав на запись",
            "Правьте от root или chown на dndlicense; файл не должен быть только для чтения.",
        ),
    ]
    for prob, fix in problems:
        p = doc.add_paragraph()
        p.add_run(f"{prob}. ").bold = True
        p.add_run(fix)

    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.add_run(
        "Репозиторий сервера: git.mailib.ru/ifontosh/DndGamePlayerLicenseServer"
    ).italic = True

    return doc


def main() -> None:
    doc = build_document()
    for path in OUT_PATHS:
        path.parent.mkdir(parents=True, exist_ok=True)
        doc.save(str(path))
        print(f"Wrote {path}")


if __name__ == "__main__":
    main()
