"""
Narinest Admin Setup Wizard
Accessible at /setup/ — only works while setup is not yet complete.
Once setup is done a .setup_complete marker file is written and the
wizard redirects all subsequent visits to /admin/.
"""
import os
import subprocess
import sys
import secrets
import string
from pathlib import Path

from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect
from django.contrib import messages

BASE_DIR = Path(__file__).resolve().parent.parent
SETUP_FLAG = BASE_DIR / '.setup_complete'
ENV_FILE = BASE_DIR / '.env'


# ──────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────

def _is_setup_complete() -> bool:
    return SETUP_FLAG.exists()


def _test_db_connection(host, port, name, user, password) -> tuple[bool, str]:
    """Try connecting with PyMySQL and return (ok, message)."""
    try:
        import pymysql
        conn = pymysql.connect(
            host=host,
            port=int(port),
            user=user,
            password=password,
            database=name,
            connect_timeout=5,
        )
        conn.close()
        return True, 'Connection successful!'
    except Exception as exc:
        return False, str(exc)


def _generate_secret_key(length: int = 50) -> str:
    alphabet = string.ascii_letters + string.digits + '!@#$%^&*(-_=+)'
    return ''.join(secrets.choice(alphabet) for _ in range(length))


def _write_env(data: dict) -> None:
    lines = [
        f'DJANGO_SECRET_KEY={data["secret_key"]}',
        f'DJANGO_DEBUG=false',
        f'DJANGO_ALLOWED_HOSTS={data["allowed_hosts"]}',
        f'DB_ENGINE=mysql',
        f'DB_HOST={data["db_host"]}',
        f'DB_PORT={data["db_port"]}',
        f'DB_NAME={data["db_name"]}',
        f'DB_USER={data["db_user"]}',
        f'DB_PASSWORD={data["db_password"]}',
        f'TIME_ZONE=UTC',
    ]
    ENV_FILE.write_text('\n'.join(lines) + '\n', encoding='utf-8')


def _run_migrations() -> tuple[bool, str]:
    try:
        result = subprocess.run(
            [sys.executable, str(BASE_DIR / 'manage.py'), 'migrate', '--run-syncdb'],
            capture_output=True, text=True, cwd=str(BASE_DIR),
            env={**os.environ, 'DJANGO_SETTINGS_MODULE': 'config.settings'},
            timeout=60,
        )
        if result.returncode == 0:
            return True, result.stdout
        return False, result.stderr or result.stdout
    except Exception as exc:
        return False, str(exc)


def _create_superuser(username: str, email: str, password: str) -> tuple[bool, str]:
    try:
        result = subprocess.run(
            [
                sys.executable, str(BASE_DIR / 'manage.py'),
                'shell', '-c',
                (
                    'from django.contrib.auth.models import User;'
                    f'User.objects.filter(username="{username}").exists() or '
                    f'User.objects.create_superuser("{username}", "{email}", "{password}")'
                ),
            ],
            capture_output=True, text=True, cwd=str(BASE_DIR),
            env={**os.environ, 'DJANGO_SETTINGS_MODULE': 'config.settings'},
            timeout=30,
        )
        if result.returncode == 0:
            return True, 'Admin user created.'
        return False, result.stderr or result.stdout
    except Exception as exc:
        return False, str(exc)


def _mark_complete() -> None:
    SETUP_FLAG.write_text('Setup completed.\n', encoding='utf-8')


# ──────────────────────────────────────────────
# Views
# ──────────────────────────────────────────────

STEPS = ['welcome', 'database', 'migrate', 'admin_user', 'done']
STEP_LABELS = [
    ('Welcome', 'welcome'),
    ('Database', 'database'),
    ('Migrate', 'migrate'),
    ('Admin User', 'admin_user'),
    ('Done', 'done'),
]


def _step_context(step: str, extra: dict = None) -> dict:
    step_index = [s[1] for s in STEP_LABELS].index(step) if step in [s[1] for s in STEP_LABELS] else 0
    ctx = {'step': step, 'step_labels': STEP_LABELS, 'step_index': step_index}
    if extra:
        ctx.update(extra)
    return ctx


def _current_step(request) -> str:
    return request.session.get('setup_step', 'welcome')


def _set_step(request, step: str) -> None:
    request.session['setup_step'] = step
    request.session.modified = True


@csrf_protect
def setup_wizard(request):
    """Main dispatch view for the setup wizard."""
    if _is_setup_complete():
        return HttpResponseRedirect('/admin/')

    step = _current_step(request)

    if request.method == 'POST':
        action = request.POST.get('action', '')

        # ── Step: welcome → database ──────────────────
        if action == 'start':
            _set_step(request, 'database')
            return HttpResponseRedirect('/setup/')

        # ── Step: database — test or save ─────────────
        if action in ('test_db', 'save_db'):
            db = {
                'host': request.POST.get('db_host', '127.0.0.1').strip(),
                'port': request.POST.get('db_port', '3306').strip(),
                'name': request.POST.get('db_name', '').strip(),
                'user': request.POST.get('db_user', '').strip(),
                'password': request.POST.get('db_password', '').strip(),
            }
            request.session['setup_db'] = db
            ok, msg = _test_db_connection(
                db['host'], db['port'], db['name'], db['user'], db['password']
            )
            if action == 'test_db':
                return render(request, 'setup/wizard.html', _step_context('database', {
                    'db': db,
                    'test_result': msg,
                    'test_ok': ok,
                }))
            if not ok:
                return render(request, 'setup/wizard.html', _step_context('database', {
                    'db': db,
                    'test_result': f'Cannot save — connection failed: {msg}',
                    'test_ok': False,
                }))
            # Write .env now so migrations can read DB settings
            allowed_hosts = request.POST.get('allowed_hosts', '*').strip() or '*'
            _write_env({
                'secret_key': _generate_secret_key(),
                'allowed_hosts': allowed_hosts,
                'db_host': db['host'],
                'db_port': db['port'],
                'db_name': db['name'],
                'db_user': db['user'],
                'db_password': db['password'],
            })
            _set_step(request, 'migrate')
            return HttpResponseRedirect('/setup/')

        # ── Step: migrate ─────────────────────────────
        if action == 'run_migrations':
            ok, output = _run_migrations()
            request.session['migrate_ok'] = ok
            request.session['migrate_output'] = output[:2000]
            if ok:
                _set_step(request, 'admin_user')
            return render(request, 'setup/wizard.html', _step_context('migrate', {
                'migrate_ok': ok,
                'migrate_output': output[:2000],
            }))

        # ── Step: admin_user ──────────────────────────
        if action == 'create_admin':
            username = request.POST.get('admin_username', '').strip()
            email = request.POST.get('admin_email', '').strip()
            password = request.POST.get('admin_password', '').strip()
            confirm = request.POST.get('admin_password_confirm', '').strip()

            errors = []
            if not username:
                errors.append('Username is required.')
            if not email or '@' not in email:
                errors.append('A valid email is required.')
            if len(password) < 8:
                errors.append('Password must be at least 8 characters.')
            if password != confirm:
                errors.append('Passwords do not match.')

            if errors:
                return render(request, 'setup/wizard.html', _step_context('admin_user', {
                    'errors': errors,
                    'username': username,
                    'email': email,
                }))

            ok, msg = _create_superuser(username, email, password)
            if not ok:
                return render(request, 'setup/wizard.html', _step_context('admin_user', {
                    'errors': [f'Failed to create user: {msg}'],
                    'username': username,
                    'email': email,
                }))
            _mark_complete()
            _set_step(request, 'done')
            return HttpResponseRedirect('/setup/')

    # ── GET: render current step ───────────────────────
    context = _step_context(step)
    if step == 'database':
        context['db'] = request.session.get('setup_db', {
            'host': '127.0.0.1', 'port': '3306',
            'name': '', 'user': '', 'password': '',
        })
    if step == 'migrate':
        context['migrate_ok'] = request.session.get('migrate_ok')
        context['migrate_output'] = request.session.get('migrate_output', '')
    if step == 'done':
        # Clear session after showing done page once
        request.session.pop('setup_step', None)
        request.session.pop('setup_db', None)
        request.session.pop('migrate_ok', None)
        request.session.pop('migrate_output', None)
    return render(request, 'setup/wizard.html', context)
