"""
Read a tenant's details straight off this server and store them.

For tenants that live on the same machine as Final Target, there is no reason
to make an HTTP request that leaves the box and comes back through a CDN. This
runs the tenant's own ft_report command, reads the JSON it prints, and writes
the result onto the tenant row.

    python manage.py ft_collect --slug mostafaei --path /home/mostafaei/public_html/backend

Run it as a user that can execute the tenant's virtualenv, normally root.
"""
import json
import os
import subprocess

from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone

from apps.tenants.models import Tenant


class Command(BaseCommand):
    help = 'Collect a same-server tenant\'s details without any HTTP request.'

    def add_arguments(self, parser):
        parser.add_argument('--slug', required=True, help='Tenant slug in this database.')
        parser.add_argument('--path', required=True, help='Path to the tenant backend directory.')
        parser.add_argument('--tenant-settings', default='config.settings.production',
                            help="The tenant's settings module.")
        parser.add_argument('--timeout', type=int, default=60)
        parser.add_argument('--dry-run', action='store_true')

    def handle(self, *args, **options):
        slug = options['slug']
        path = options['path'].rstrip('/')

        tenant = Tenant.objects.filter(slug=slug).first()
        if not tenant:
            existing = ', '.join(Tenant.objects.values_list('slug', flat=True))
            raise CommandError(f'No tenant with slug "{slug}". Known slugs: {existing}')

        python = os.path.join(path, 'venv', 'bin', 'python')
        manage = os.path.join(path, 'manage.py')
        for required in (python, manage):
            if not os.path.exists(required):
                raise CommandError(f'Not found: {required}')

        # Run with a cleaned environment. Anything inherited from this process
        # would override what the tenant reads from its own .env, which would
        # make it report Final Target's database and settings rather than its own.
        child_env = {
            k: v for k, v in os.environ.items()
            if not (
                k.startswith(('DJANGO_', 'FT_', 'DB_'))
                or k in {'DATABASE_URL', 'SECRET_KEY', 'ALLOWED_HOSTS', 'CMS_VERSION'}
            )
        }

        try:
            result = subprocess.run(
                [python, manage, 'ft_report', f'--settings={options["tenant_settings"]}'],
                cwd=path, capture_output=True, text=True, timeout=options['timeout'],
                env=child_env,
            )
        except subprocess.TimeoutExpired:
            raise CommandError('The tenant took too long to respond.')

        if result.returncode != 0:
            raise CommandError(
                f'The tenant command failed ({result.returncode}):\n'
                f'{(result.stderr or result.stdout)[:500]}'
            )

        # ft_report prints one JSON line, but Django may emit warnings first,
        # so take the last line that parses.
        report = None
        for line in reversed([l for l in result.stdout.splitlines() if l.strip()]):
            try:
                report = json.loads(line)
                break
            except ValueError:
                continue
        if report is None:
            raise CommandError(f'No JSON in the tenant output:\n{result.stdout[:500]}')

        fields = {
            'reported_version': report.get('ft_version', ''),
            'os_name':          report.get('os_name', ''),
            'os_version':       report.get('os_version', ''),
            'python_version':   report.get('python_version', ''),
            'django_version':   report.get('django_version', ''),
            'db_engine':        report.get('db_engine', ''),
        }

        self.stdout.write(f'{tenant.name}:')
        for key, value in fields.items():
            self.stdout.write(f'  {key:<17} {value or "(blank)"}')

        if options['dry_run']:
            self.stdout.write(self.style.WARNING('Dry run, nothing saved.'))
            return

        for key, value in fields.items():
            setattr(tenant, key, value)
        tenant.last_checkin_at = timezone.now()
        tenant.save(update_fields=list(fields) + ['last_checkin_at'])

        self.stdout.write(self.style.SUCCESS(f'Stored. {tenant.name} now shows as seen just now.'))
