"""
Multi-step web installer views.
"""
from django.contrib import messages
from django.core.management import call_command
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views import View

from apps.installer.utils import (
    all_requirements_ok,
    check_requirements,
    generate_secret_key,
    is_installed,
    mark_installed,
    sanitize_hosts,
    test_mysql_connection,
    validate_admin,
    write_env,
)


SESSION_KEY = 'installer_data'


def _get_data(request) -> dict:
    return request.session.get(SESSION_KEY, {})


def _save_data(request, **kwargs):
    data = _get_data(request)
    data.update(kwargs)
    request.session[SESSION_KEY] = data
    request.session.modified = True


class InstallerWelcomeView(View):
    def get(self, request):
        if is_installed():
            return redirect('home')
        return render(request, 'installer/welcome.html', {
            'step': 1,
            'total_steps': 5,
        })


class InstallerRequirementsView(View):
    def get(self, request):
        if is_installed():
            return redirect('home')
        checks = check_requirements()
        return render(request, 'installer/requirements.html', {
            'step': 2,
            'total_steps': 5,
            'checks': checks,
            'all_ok': all_requirements_ok(checks),
        })

    def post(self, request):
        if not all_requirements_ok():
            messages.error(request, 'Fix failed requirements before continuing.')
            return redirect('installer:requirements')
        return redirect('installer:database')


class InstallerDatabaseView(View):
    def get(self, request):
        if is_installed():
            return redirect('home')
        data = _get_data(request)
        return render(request, 'installer/database.html', {
            'step': 3,
            'total_steps': 5,
            'data': data,
        })

    def post(self, request):
        engine = request.POST.get('db_engine', 'sqlite3')
        payload = {
            'db_engine': engine,
            'db_name': request.POST.get('db_name', 'db.sqlite3').strip(),
            'db_user': request.POST.get('db_user', '').strip(),
            'db_password': request.POST.get('db_password', ''),
            'db_host': request.POST.get('db_host', '127.0.0.1').strip(),
            'db_port': request.POST.get('db_port', '3306').strip(),
        }

        if engine == 'mysql':
            if not payload['db_name']:
                messages.error(request, 'Database name is required for MySQL.')
                return redirect('installer:database')
            ok, detail = test_mysql_connection(
                payload['db_user'], payload['db_password'],
                payload['db_host'], payload['db_port'], payload['db_name'],
            )
            if not ok:
                messages.error(request, f'MySQL connection failed: {detail}')
                _save_data(request, **payload)
                return redirect('installer:database')
            messages.success(request, 'MySQL connection successful.')
        else:
            payload['db_name'] = payload['db_name'] or 'db.sqlite3'

        _save_data(request, **payload)
        return redirect('installer:site')


class InstallerSiteView(View):
    def get(self, request):
        if is_installed():
            return redirect('home')
        data = _get_data(request)
        return render(request, 'installer/site.html', {
            'step': 4,
            'total_steps': 5,
            'data': data,
            'secret_key': data.get('secret_key') or generate_secret_key(),
        })

    def post(self, request):
        payload = {
            'institute_name': request.POST.get('institute_name', 'Digital Academy').strip() or 'Digital Academy',
            'tagline': request.POST.get('tagline', '').strip(),
            'allowed_hosts': sanitize_hosts(request.POST.get('allowed_hosts', 'localhost,127.0.0.1')),
            'admin_username': request.POST.get('admin_username', '').strip(),
            'admin_email': request.POST.get('admin_email', '').strip(),
            'admin_password': request.POST.get('admin_password', ''),
            'admin_full_name': request.POST.get('admin_full_name', 'Site Admin').strip() or 'Site Admin',
            'secret_key': request.POST.get('secret_key') or generate_secret_key(),
            'debug': 'True' if request.POST.get('debug') == 'on' else 'False',
            'seed_demo': request.POST.get('seed_demo') == 'on',
        }
        errors = validate_admin(
            payload['admin_username'],
            payload['admin_email'],
            payload['admin_password'],
        )
        if errors:
            for err in errors:
                messages.error(request, err)
            _save_data(request, **payload)
            return redirect('installer:site')

        _save_data(request, **payload)
        return redirect('installer:run')


class InstallerRunView(View):
    """Write config, migrate, create admin, lock installer."""

    def get(self, request):
        if is_installed():
            return redirect('home')
        data = _get_data(request)
        if not data.get('db_engine') or not data.get('admin_username'):
            messages.warning(request, 'Please complete previous steps first.')
            return redirect('installer:welcome')
        return render(request, 'installer/run.html', {
            'step': 5,
            'total_steps': 5,
            'data': data,
        })

    def post(self, request):
        if is_installed():
            return redirect('home')

        data = _get_data(request)
        if not data.get('admin_username'):
            messages.error(request, 'Missing installer data. Start again.')
            return redirect('installer:welcome')

        try:
            # 1) Write .env
            write_env({
                'debug': data.get('debug', 'True'),
                'secret_key': data.get('secret_key'),
                'allowed_hosts': data.get('allowed_hosts'),
                'db_engine': data.get('db_engine', 'sqlite3'),
                'db_name': data.get('db_name', 'db.sqlite3'),
                'db_user': data.get('db_user', ''),
                'db_password': data.get('db_password', ''),
                'db_host': data.get('db_host', '127.0.0.1'),
                'db_port': data.get('db_port', '3306'),
                'institute_name': data.get('institute_name'),
            })

            # Reload dotenv into process for subsequent DB ops when possible
            from dotenv import load_dotenv
            from pathlib import Path
            from django.conf import settings as dj_settings
            load_dotenv(Path(dj_settings.BASE_DIR) / '.env', override=True)

            # Reconfigure DATABASES for this process if MySQL selected
            engine = data.get('db_engine', 'sqlite3')
            if engine == 'mysql':
                import pymysql
                pymysql.install_as_MySQLdb()
                dj_settings.DATABASES['default'] = {
                    'ENGINE': 'django.db.backends.mysql',
                    'NAME': data.get('db_name'),
                    'USER': data.get('db_user') or 'root',
                    'PASSWORD': data.get('db_password') or '',
                    'HOST': data.get('db_host') or '127.0.0.1',
                    'PORT': data.get('db_port') or '3306',
                    'OPTIONS': {
                        'charset': 'utf8mb4',
                        'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
                    },
                }
            else:
                from pathlib import Path
                dj_settings.DATABASES['default'] = {
                    'ENGINE': 'django.db.backends.sqlite3',
                    'NAME': Path(dj_settings.BASE_DIR) / (data.get('db_name') or 'db.sqlite3'),
                }

            # Close old DB connections so new settings take effect
            from django.db import connections
            connections.close_all()

            # 2) Migrate
            call_command('migrate', interactive=False, verbosity=0)

            # 3) Site settings
            from apps.dashboard.models import SiteSettings
            site = SiteSettings.load()
            site.institute_name = data.get('institute_name') or site.institute_name
            site.tagline = data.get('tagline') or site.tagline
            site.save()

            # 4) Admin user
            from apps.accounts.models import CustomUser
            username = data['admin_username']
            if CustomUser.objects.filter(username=username).exists():
                admin = CustomUser.objects.get(username=username)
                admin.email = data['admin_email']
                admin.full_name = data.get('admin_full_name') or admin.full_name
                admin.role = 'admin'
                admin.is_staff = True
                admin.is_superuser = True
                admin.set_password(data['admin_password'])
                admin.save()
            else:
                admin = CustomUser.objects.create_superuser(
                    username=username,
                    email=data['admin_email'],
                    password=data['admin_password'],
                )
                admin.full_name = data.get('admin_full_name') or 'Site Admin'
                admin.role = 'admin'
                admin.save()

            # 5) Optional demo seed
            if data.get('seed_demo'):
                try:
                    call_command('seed_data', verbosity=0)
                except Exception:
                    pass

            # 6) Lock installer
            mark_installed()
            request.session.pop(SESSION_KEY, None)

            # Store credentials flash for complete page only (session)
            request.session['install_complete'] = {
                'username': username,
                'institute': site.institute_name,
            }
            return redirect('installer:complete')

        except Exception as exc:
            messages.error(request, f'Installation failed: {exc}')
            return redirect('installer:run')


class InstallerCompleteView(View):
    def get(self, request):
        info = request.session.pop('install_complete', None)
        return render(request, 'installer/complete.html', {
            'step': 5,
            'total_steps': 5,
            'info': info,
            'installed': is_installed(),
        })
