from __future__ import annotations

import argparse
import subprocess
import sys
import textwrap
from pathlib import Path


ROOT = Path.cwd()
MIN_PYTHON = (3, 12)


def norm(text: str) -> str:
    return textwrap.dedent(text).lstrip("\n").rstrip() + "\n"


def write(path: Path, content: str, overwrite: bool = False) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    if path.exists() and not overwrite:
        print(f"[SKIP] {path}")
        return
    path.write_text(norm(content), encoding="utf-8")
    print(f"[WRITE] {path}")


def touch(path: Path) -> None:
    write(path, '"""Package marker."""\n', overwrite=False)


def run(cmd: list[str], cwd: Path | None = None, allow_fail: bool = False) -> int:
    print("[RUN ]", " ".join(cmd))
    try:
        completed = subprocess.run(cmd, cwd=cwd, check=not allow_fail)
        return completed.returncode
    except subprocess.CalledProcessError as exc:
        print(f"[FAIL] {' '.join(cmd)} -> {exc.returncode}")
        if not allow_fail:
            raise
        return exc.returncode


def ensure_python() -> None:
    current = sys.version_info[:3]
    if current < MIN_PYTHON:
        req = ".".join(map(str, MIN_PYTHON))
        got = ".".join(map(str, current))
        raise SystemExit(
            f"Python {req}+ is required. Current interpreter: {got}. "
            f"Run with /opt/homebrew/bin/python3.12 bootstrap_project.py --with-venv --with-install"
        )


def create_dirs() -> None:
    for rel in [
        "app/src/bootstrap",
        "app/src/core",
        "app/src/telegram/handlers",
        "app/src/telegram/keyboards",
        "app/src/telegram/callbacks",
        "app/src/trading",
        "app/src/storage",
        "app/src/integrations",
        "app/src/shared",
        "app/tests",
        "docs/architecture",
        "docs/stages",
        "docs/decisions",
        "infra/docker",
        "infra/compose",
        ".vscode",
    ]:
        path = ROOT / rel
        path.mkdir(parents=True, exist_ok=True)
        print(f"[DIR ] {path}")


def create_init_files() -> None:
    for rel in [
        "app/src/__init__.py",
        "app/src/bootstrap/__init__.py",
        "app/src/core/__init__.py",
        "app/src/telegram/__init__.py",
        "app/src/telegram/handlers/__init__.py",
        "app/src/telegram/keyboards/__init__.py",
        "app/src/telegram/callbacks/__init__.py",
        "app/src/trading/__init__.py",
        "app/src/storage/__init__.py",
        "app/src/integrations/__init__.py",
        "app/src/shared/__init__.py",
        "app/tests/__init__.py",
    ]:
        touch(ROOT / rel)


def create_root_files() -> None:
    write(ROOT / ".gitignore", """
    __pycache__/
    *.py[cod]
    .DS_Store
    .pytest_cache/
    .mypy_cache/
    .ruff_cache/

    app/.env
    app/.venv/
    .venv/
    venv/

    logs/
    data/
    """)
    write(ROOT / ".vscode" / "settings.json", """
    {
      "python.defaultInterpreterPath": "app/.venv/bin/python"
    }
    """)
    write(ROOT / "README.md", """
    # dzentra_bot

    Telegram-бот для автоторговли криптовалютой.

    ## Bootstrap v2
    Это стабильный стартовый каркас проекта с:
    - чистой структурой каталогов
    - компактным верхним меню
    - базовыми handlers
    - документацией
    - Docker-файлами
    - опциональным созданием `.venv`, установкой зависимостей и первым git commit

    ## Верхнее меню
    - 🏠 Главная
    - 📈 Рынок
    - 💼 Портфель
    - ⚡ Торговля
    - 🤖 Авто
    - 📒 Журнал
    - ⚙️ Система

    ## Рекомендуемый запуск
    ```bash
    /opt/homebrew/bin/python3.12 bootstrap_project.py --with-venv --with-install
    ```

    ## После bootstrap
    ```bash
    cd app
    source .venv/bin/activate
    python -m src.main
    ```
    """)


def create_app_files() -> None:
    write(ROOT / "app" / ".env.example", """
    BOT_TOKEN=PUT_YOUR_TELEGRAM_BOT_TOKEN_HERE
    BOT_PARSE_MODE=HTML
    APP_ENV=dev
    LOG_LEVEL=INFO
    TZ=Europe/Madrid
    """)
    write(ROOT / "app" / "requirements.txt", """
    aiogram==3.13.1
    python-dotenv==1.0.1
    """)
    write(ROOT / "app" / "README.md", """
    # app

    Здесь находятся исходный код приложения, env-файлы и зависимости.
    """)


def create_core_files() -> None:
    write(ROOT / "app/src/core/config.py", """
    from __future__ import annotations

    import os
    from dataclasses import dataclass
    from pathlib import Path

    from dotenv import load_dotenv


    BASE_DIR = Path(__file__).resolve().parents[2]
    ENV_FILE = BASE_DIR / ".env"
    load_dotenv(ENV_FILE)


    @dataclass(slots=True)
    class Settings:
        bot_token: str
        bot_parse_mode: str
        app_env: str
        log_level: str
        tz: str


    def load_settings() -> Settings:
        bot_token = os.getenv("BOT_TOKEN", "").strip()
        if not bot_token:
            raise RuntimeError("BOT_TOKEN is not set in app/.env")

        return Settings(
            bot_token=bot_token,
            bot_parse_mode=os.getenv("BOT_PARSE_MODE", "HTML").strip() or "HTML",
            app_env=os.getenv("APP_ENV", "dev").strip() or "dev",
            log_level=os.getenv("LOG_LEVEL", "INFO").strip().upper() or "INFO",
            tz=os.getenv("TZ", "Europe/Madrid").strip() or "Europe/Madrid",
        )
    """)
    write(ROOT / "app/src/core/constants.py", """
    APP_NAME = "Dzentra Bot"
    APP_VERSION = "2.0.0"
    """)
    write(ROOT / "app/src/core/exceptions.py", """
    class AppError(Exception):
        \"\"\"Base application exception.\"\"\"
    """)


def create_bootstrap_files() -> None:
    write(ROOT / "app/src/bootstrap/logging.py", """
    from __future__ import annotations

    import logging


    def setup_logging(log_level: str) -> None:
        logging.basicConfig(
            level=getattr(logging, log_level.upper(), logging.INFO),
            format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
        )
    """)
    write(ROOT / "app/src/bootstrap/app_factory.py", """
    from __future__ import annotations

    from aiogram import Bot, Dispatcher
    from aiogram.client.default import DefaultBotProperties

    from src.bootstrap.logging import setup_logging
    from src.core.config import load_settings
    from src.telegram.routers import setup_routers


    def create_app() -> tuple[Bot, Dispatcher]:
        settings = load_settings()
        setup_logging(settings.log_level)

        bot = Bot(
            token=settings.bot_token,
            default=DefaultBotProperties(parse_mode=settings.bot_parse_mode),
        )
        dispatcher = Dispatcher()
        setup_routers(dispatcher)

        return bot, dispatcher
    """)


def create_telegram_files() -> None:
    write(ROOT / "app/src/telegram/menus.py", """
    MAIN_MENU_TEXT = (
        "<b>Dzentra Bot</b>\\n\\n"
        "Новый каркас проекта успешно создан.\\n\\n"
        "Выбери раздел через меню ниже."
    )

    HOME_TEXT = (
        "<b>🏠 Главная</b>\\n\\n"
        "Это главный экран бота.\\n\\n"
        "Сейчас здесь отображается базовый статус:\\n"
        "- бот запущен\\n"
        "- меню подключено\\n"
        "- handlers работают\\n"
        "- проект на этапе Bootstrap v2\\n"
    )

    SYSTEM_TEXT = (
        "<b>⚙️ Система</b>\\n\\n"
        "Системный экран.\\n\\n"
        "<b>Справка</b>\\n"
        "/start — запуск\\n"
        "/menu — показать меню\\n"
        "/help — краткая справка\\n"
    )

    MARKET_TEXT = "<b>📈 Рынок</b>\\n\\nРаздел пока в разработке."
    PORTFOLIO_TEXT = "<b>💼 Портфель</b>\\n\\nРаздел пока в разработке."
    TRADE_TEXT = "<b>⚡ Торговля</b>\\n\\nРаздел пока в разработке."
    AUTO_TEXT = "<b>🤖 Авто</b>\\n\\nРаздел пока в разработке."
    JOURNAL_TEXT = "<b>📒 Журнал</b>\\n\\nРаздел пока в разработке."
    """)
    write(ROOT / "app/src/telegram/keyboards/reply.py", """
    from aiogram.types import KeyboardButton, ReplyKeyboardMarkup


    def build_main_menu_keyboard() -> ReplyKeyboardMarkup:
        return ReplyKeyboardMarkup(
            keyboard=[
                [
                    KeyboardButton(text="🏠 Главная"),
                    KeyboardButton(text="📈 Рынок"),
                    KeyboardButton(text="💼 Портфель"),
                ],
                [
                    KeyboardButton(text="⚡ Торговля"),
                    KeyboardButton(text="🤖 Авто"),
                    KeyboardButton(text="📒 Журнал"),
                ],
                [
                    KeyboardButton(text="⚙️ Система"),
                ],
            ],
            resize_keyboard=True,
            input_field_placeholder="Выбери раздел...",
        )
    """)
    write(ROOT / "app/src/telegram/routers.py", """
    from aiogram import Dispatcher

    from src.telegram.handlers.auto import router as auto_router
    from src.telegram.handlers.home import router as home_router
    from src.telegram.handlers.journal import router as journal_router
    from src.telegram.handlers.market import router as market_router
    from src.telegram.handlers.portfolio import router as portfolio_router
    from src.telegram.handlers.start import router as start_router
    from src.telegram.handlers.system import router as system_router
    from src.telegram.handlers.trade import router as trade_router


    def setup_routers(dispatcher: Dispatcher) -> None:
        dispatcher.include_router(start_router)
        dispatcher.include_router(home_router)
        dispatcher.include_router(market_router)
        dispatcher.include_router(portfolio_router)
        dispatcher.include_router(trade_router)
        dispatcher.include_router(auto_router)
        dispatcher.include_router(journal_router)
        dispatcher.include_router(system_router)
    """)
    write(ROOT / "app/src/telegram/handlers/start.py", """
    from aiogram import F, Router
    from aiogram.filters import Command
    from aiogram.types import Message

    from src.telegram.keyboards.reply import build_main_menu_keyboard
    from src.telegram.menus import MAIN_MENU_TEXT, SYSTEM_TEXT


    router = Router(name="start")


    @router.message(Command("start"))
    async def cmd_start(message: Message) -> None:
        await message.answer(MAIN_MENU_TEXT, reply_markup=build_main_menu_keyboard())


    @router.message(Command("menu"))
    async def cmd_menu(message: Message) -> None:
        await message.answer(MAIN_MENU_TEXT, reply_markup=build_main_menu_keyboard())


    @router.message(Command("help"))
    async def cmd_help(message: Message) -> None:
        await message.answer(SYSTEM_TEXT, reply_markup=build_main_menu_keyboard())


    @router.message(F.text == "Меню")
    async def menu_shortcut(message: Message) -> None:
        await message.answer(MAIN_MENU_TEXT, reply_markup=build_main_menu_keyboard())
    """)
    write(ROOT / "app/src/telegram/handlers/home.py", """
    from aiogram import F, Router
    from aiogram.types import Message

    from src.telegram.menus import HOME_TEXT


    router = Router(name="home")


    @router.message(F.text == "🏠 Главная")
    async def open_home(message: Message) -> None:
        await message.answer(HOME_TEXT)
    """)
    write(ROOT / "app/src/telegram/handlers/market.py", """
    from aiogram import F, Router
    from aiogram.types import Message

    from src.telegram.menus import MARKET_TEXT


    router = Router(name="market")


    @router.message(F.text == "📈 Рынок")
    async def open_market(message: Message) -> None:
        await message.answer(MARKET_TEXT)
    """)
    write(ROOT / "app/src/telegram/handlers/portfolio.py", """
    from aiogram import F, Router
    from aiogram.types import Message

    from src.telegram.menus import PORTFOLIO_TEXT


    router = Router(name="portfolio")


    @router.message(F.text == "💼 Портфель")
    async def open_portfolio(message: Message) -> None:
        await message.answer(PORTFOLIO_TEXT)
    """)
    write(ROOT / "app/src/telegram/handlers/trade.py", """
    from aiogram import F, Router
    from aiogram.types import Message

    from src.telegram.menus import TRADE_TEXT


    router = Router(name="trade")


    @router.message(F.text == "⚡ Торговля")
    async def open_trade(message: Message) -> None:
        await message.answer(TRADE_TEXT)
    """)
    write(ROOT / "app/src/telegram/handlers/auto.py", """
    from aiogram import F, Router
    from aiogram.types import Message

    from src.telegram.menus import AUTO_TEXT


    router = Router(name="auto")


    @router.message(F.text == "🤖 Авто")
    async def open_auto(message: Message) -> None:
        await message.answer(AUTO_TEXT)
    """)
    write(ROOT / "app/src/telegram/handlers/journal.py", """
    from aiogram import F, Router
    from aiogram.types import Message

    from src.telegram.menus import JOURNAL_TEXT


    router = Router(name="journal")


    @router.message(F.text == "📒 Журнал")
    async def open_journal(message: Message) -> None:
        await message.answer(JOURNAL_TEXT)
    """)
    write(ROOT / "app/src/telegram/handlers/system.py", """
    import platform

    from aiogram import F, Router
    from aiogram.types import Message

    from src.core.constants import APP_NAME, APP_VERSION
    from src.telegram.menus import SYSTEM_TEXT


    router = Router(name="system")


    @router.message(F.text.in_({"⚙️ Система", "⚙ Система"}))
    async def open_system(message: Message) -> None:
        runtime_info = (
            "\\n\\n<b>Runtime</b>\\n"
            f"- app: {APP_NAME} {APP_VERSION}\\n"
            f"- python: {platform.python_version()}\\n"
            f"- os: {platform.system()} {platform.release()}"
        )
        await message.answer(SYSTEM_TEXT + runtime_info)
    """)


def create_misc_files() -> None:
    write(ROOT / "app/src/trading/README.md", """
    Здесь будет торговая бизнес-логика.
    """)
    write(ROOT / "app/src/storage/README.md", """
    Здесь будет слой доступа к данным.
    """)
    write(ROOT / "app/src/integrations/README.md", """
    Здесь будут внешние интеграции, например биржа.
    """)
    write(ROOT / "app/src/shared/README.md", """
    Здесь будут общие утилиты.
    """)
    write(ROOT / "app/tests/test_smoke.py", """
    def test_smoke() -> None:
        assert True
    """)
    write(ROOT / "app/src/main.py", """
    import asyncio

    from src.bootstrap.app_factory import create_app


    async def main() -> None:
        bot, dispatcher = create_app()
        await dispatcher.start_polling(bot)


    if __name__ == "__main__":
        asyncio.run(main())
    """)
    write(ROOT / "infra/docker/Dockerfile", """
    FROM python:3.12-slim

    ENV PYTHONDONTWRITEBYTECODE=1
    ENV PYTHONUNBUFFERED=1

    WORKDIR /project

    COPY app /project/app
    RUN pip install --no-cache-dir -r /project/app/requirements.txt

    WORKDIR /project/app
    CMD ["python", "-m", "src.main"]
    """)
    write(ROOT / "infra/compose/docker-compose.yml", """
    services:
      bot:
        build:
          context: ../..
          dockerfile: infra/docker/Dockerfile
        container_name: dzentra_bot
        restart: unless-stopped
        env_file:
          - ../../app/.env
    """)


def create_docs() -> None:
    write(ROOT / "docs/changelog.md", """
    # Changelog

    ## v2.0.0
    - создан стабильный bootstrap v2
    - добавлена чистая структура проекта
    - добавлено компактное верхнее меню 3 / 3 / 1
    - добавлены базовые handlers
    - help перенесен в раздел `Система`
    - добавлены Docker-файлы
    - добавлена стартовая документация
    """)
    write(ROOT / "docs/architecture/overview.md", """
    # Architecture Overview

    Проект строится как modular monolith с разделением по слоям:

    - `telegram` — меню, handlers, routers
    - `bootstrap` — сборка приложения
    - `core` — конфигурация и базовые сущности
    - `trading` — бизнес-логика торговли
    - `storage` — доступ к данным
    - `integrations` — внешние API
    - `shared` — общие утилиты
    """)
    write(ROOT / "docs/architecture/project_structure.md", """
    # Project Structure

    ## Корневые папки
    - `app/` — код приложения
    - `docs/` — документация
    - `infra/` — Docker и compose

    ## Внутри `app/src`
    - `bootstrap/`
    - `core/`
    - `telegram/`
    - `trading/`
    - `storage/`
    - `integrations/`
    - `shared/`
    """)
    write(ROOT / "docs/architecture/telegram_menu.md", """
    # Telegram Menu

    Верхнее меню bootstrap v2:

    1. `🏠 Главная` `📈 Рынок` `💼 Портфель`
    2. `⚡ Торговля` `🤖 Авто` `📒 Журнал`
    3. `⚙️ Система`

    На старте реально работают:
    - `/start`
    - `/menu`
    - `/help`
    - все кнопки верхнего меню
    """)
    write(ROOT / "docs/stages/stage-01-bootstrap.md", """
    # Stage 01 — Bootstrap

    ## Цель
    Получить стабильный стартовый каркас проекта без архитектурной каши.

    ## Что есть
    - структура каталогов
    - env
    - зависимости
    - базовый aiogram-бот
    - reply-меню
    - handlers
    - docs
    - Docker
    """)
    write(ROOT / "docs/decisions/0001-project-structure.md", """
    # 0001 — Project Structure

    Решение:
    использовать modular monolith с разбиением на `core`, `bootstrap`, `telegram`,
    `trading`, `storage`, `integrations`, `shared`.
    """)
    write(ROOT / "docs/decisions/0002-menu-design.md", """
    # 0002 — Menu Design

    Решение:
    использовать компактное верхнее меню 3 / 3 / 1:
    - Главная / Рынок / Портфель
    - Торговля / Авто / Журнал
    - Система

    Справка перенесена в раздел `Система`.
    """)
    write(ROOT / "docs/decisions/0003-python-workflow.md", """
    # 0003 — Python Workflow

    Решение:
    - использовать Python 3.12
    - создавать `.venv` внутри `app/`
    - не использовать conda/base для этого проекта
    - в VS Code выбирать `app/.venv/bin/python`
    """)


def maybe_create_env() -> None:
    env = ROOT / "app/.env"
    example = ROOT / "app/.env.example"
    if not env.exists():
        env.write_text(example.read_text(encoding="utf-8"), encoding="utf-8")
        print("[WRITE] app/.env created from .env.example")
    else:
        print("[SKIP] app/.env already exists")


def create_venv_and_install(with_venv: bool, with_install: bool) -> None:
    app_dir = ROOT / "app"
    venv_dir = app_dir / ".venv"

    if with_venv and not venv_dir.exists():
        run([sys.executable, "-m", "venv", str(venv_dir)])

    if with_install:
        if not venv_dir.exists():
            raise RuntimeError("app/.venv does not exist. Use --with-venv together with --with-install.")
        pip_path = venv_dir / "bin" / "pip"
        run([str(pip_path), "install", "--upgrade", "pip"], cwd=app_dir)
        run([str(pip_path), "install", "-r", "requirements.txt"], cwd=app_dir)


def setup_git(with_git: bool) -> None:
    if not with_git:
        return
    if not (ROOT / ".git").exists():
        run(["git", "init"], cwd=ROOT)
    run(["git", "add", "."], cwd=ROOT)
    status = subprocess.run(["git", "diff", "--cached", "--quiet"], cwd=ROOT, check=False)
    if status.returncode != 0:
        run(["git", "commit", "-m", "bootstrap v2"], cwd=ROOT, allow_fail=True)
    else:
        print("[INFO] Nothing new to commit.")


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Stable bootstrap v2 for dzentra_bot.")
    parser.add_argument("--with-venv", action="store_true", help="Create app/.venv")
    parser.add_argument("--with-install", action="store_true", help="Install requirements into app/.venv")
    parser.add_argument("--with-git", action="store_true", help="Initialize git and create first commit")
    return parser.parse_args()


def main() -> None:
    ensure_python()
    args = parse_args()

    if args.with_install and not args.with_venv:
        raise SystemExit("--with-install requires --with-venv")

    print("\n=== dzentra_bot bootstrap v2 ===\n")
    print(f"[INFO] Interpreter: {sys.executable}")
    print(f"[INFO] Python: {sys.version.split()[0]}")

    create_dirs()
    create_init_files()
    create_root_files()
    create_app_files()
    create_core_files()
    create_bootstrap_files()
    create_telegram_files()
    create_misc_files()
    create_docs()
    maybe_create_env()
    create_venv_and_install(args.with_venv, args.with_install)
    setup_git(args.with_git)

    print("\nBootstrap v2 completed.\n")
    print("Next steps:")
    print("1. Open app/.env and fill BOT_TOKEN")
    print("2. cd app")
    if args.with_venv:
        print("3. source .venv/bin/activate")
    else:
        print("3. Create venv later with --with-venv --with-install")
    print("4. python -m src.main")
    print("5. In VS Code use interpreter: app/.venv/bin/python")
    print("6. For Synology: sudo docker compose -f infra/compose/docker-compose.yml up --build -d")


if __name__ == "__main__":
    main()
