"""
Set up the tenant row that this install serves.

Every install has exactly one Tenant row it belongs to, and that row must carry
is_master=True. Not because the site is the operator's own, but because the flag means
"the tenant this install serves". Without it _get_master_tenant() finds nothing,
falls back to the _DevTenant placeholder, and the whole site renders under the
operator's name and colours while looking, from the outside, like data corruption.

That is not a hypothetical. It happened to mostafaei.com on 20 August 2026 and
cost most of a day to find.

Run once during provisioning:

    venv/bin/python manage.py ft_tenant_init \
        --name "MOSTAFAEI" \
        --domain mostafaei.com \
        --panel panel.mostafaei.com \
        --settings=config.settings.production

Safe to run again. It repairs an existing row rather than creating a second one,
and it never writes content: no seeded pages, no footer links, no settings from
another site. A new panel starts empty on purpose.
"""
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils.text import slugify


class Command(BaseCommand):
    help = 'Create or repair the Tenant row this install serves, and flag it as master of this install.'

    def add_arguments(self, parser):
        parser.add_argument('--name', required=True, help='Site name as it should appear, e.g. "MOSTAFAEI".')
        parser.add_argument('--domain', required=True, help='Public domain, e.g. mostafaei.com')
        parser.add_argument('--panel', default='', help='Panel hostname, e.g. panel.mostafaei.com')
        parser.add_argument('--slug', default='', help="Slug as it exists in the fleet master database. Defaults to the name.")
        parser.add_argument('--plan', default='starter', help='Plan key. The fleet master sends the real one on the next check-in.')
        parser.add_argument(
            '--modules', default='',
            help='Comma separated module keys to enable. Default is whatever the plan grants.',
        )
        parser.add_argument(
            '--list-modules', action='store_true',
            help='Print the module keys and exit, so you know what --modules accepts.',
        )
        # The credit line at the foot of every public page. Passed in rather
        # than hardcoded, because this command ships inside the blank template
        # and must not carry anyone's name.
        parser.add_argument('--builder-name', default='', help='Sub footer credit, e.g. "Designed & Developed by Example.com".')
        parser.add_argument('--builder-url',  default='', help='Where that credit links to.')

    @transaction.atomic
    def handle(self, *args, **options):
        from apps.tenants.models import (
            Tenant, TenantModule, MODULE_CHOICES, PLAN_MODULES, ALWAYS_ON_MODULES,
        )

        if options['list_modules']:
            for key, label in MODULE_CHOICES:
                always = ' (always on)' if key in ALWAYS_ON_MODULES else ''
                self.stdout.write(f'  {key:<14} {label}{always}')
            return

        name = options['name'].strip()
        domain = options['domain'].strip().lower().rstrip('/')
        panel = (options['panel'] or f'panel.{domain}').strip().lower()
        slug = slugify(options['slug'] or name)
        plan = options['plan'].strip()

        valid_plans = {key for key, _ in Tenant.PLAN_CHOICES}
        if plan not in valid_plans:
            raise CommandError(f'Unknown plan "{plan}". Expected one of: {", ".join(sorted(valid_plans))}.')

        existing = Tenant.objects.count()
        if existing > 1:
            # More than one row means this install is not a plain tenant, and
            # guessing which one it serves is exactly the wrong move.
            rows = ', '.join(f'{t.pk}:{t.slug}' for t in Tenant.objects.all()[:10])
            raise CommandError(
                f'This database holds {existing} tenant rows ({rows}). '
                f'A tenant install should hold one. Refusing to guess which is the local site.'
            )

        tenant = Tenant.objects.first()
        created = tenant is None
        if created:
            tenant = Tenant(slug=slug)

        tenant.name = name
        # The slug is the identity the fleet key is signed against. Renaming it
        # on a rerun would silently break check-in, so an existing row keeps its
        # slug unless --slug says otherwise.
        if created or options['slug']:
            tenant.slug = slug
        elif tenant.slug != slug:
            self.stdout.write(self.style.WARNING(
                f'Keeping the existing slug "{tenant.slug}". Pass --slug to change it.'
            ))
        tenant.primary_domain = domain
        tenant.cms_subdomain = panel
        if created:
            tenant.plan = plan
        tenant.is_active = True
        tenant.is_master = True
        tenant.save()

        # Any other row claiming master would put the install back into the
        # ambiguous state this command exists to prevent.
        others = Tenant.objects.filter(is_master=True).exclude(pk=tenant.pk)
        if others.exists():
            others.update(is_master=False)
            self.stdout.write(self.style.WARNING('Cleared the master flag from another row.'))

        # Modules. Explicit list wins, otherwise the plan decides. Always-on
        # modules are added regardless, since the panel assumes they exist.
        requested = {m.strip() for m in options['modules'].split(',') if m.strip()}
        valid_modules = {key for key, _ in MODULE_CHOICES}
        unknown = requested - valid_modules
        if unknown:
            raise CommandError(
                f'Unknown module(s): {", ".join(sorted(unknown))}. '
                f'Run with --list-modules to see the keys.'
            )
        wanted = (requested or set(PLAN_MODULES.get(tenant.plan, set()))) | set(ALWAYS_ON_MODULES)

        enabled_now = []
        for key in sorted(wanted):
            row, was_created = TenantModule.objects.get_or_create(
                tenant=tenant, module=key, defaults={'is_enabled': True}
            )
            if not was_created and not row.is_enabled:
                row.is_enabled = True
                row.save(update_fields=['is_enabled'])
            enabled_now.append(key)

        # Anything not asked for is switched off, so a rerun with a smaller
        # list actually shrinks the site rather than only ever growing it.
        turned_off = TenantModule.objects.filter(
            tenant=tenant, is_enabled=True
        ).exclude(module__in=wanted).update(is_enabled=False)

        # The builder credit, written straight into this install's Settings so
        # the sub footer renders on the very first request rather than after
        # somebody remembers to fill a form in.
        if options['builder_name'] or options['builder_url']:
            try:
                from apps.pages.models import SiteSettings
                row = SiteSettings.load(tenant)
                if options['builder_name']:
                    row.builder_name = options['builder_name']
                if options['builder_url']:
                    row.builder_url = options['builder_url']
                row.save(update_fields=['builder_name', 'builder_url'])
            except Exception as exc:
                self.stdout.write(self.style.WARNING(
                    f'Tenant created, but the builder credit could not be saved: {exc}'
                ))

        verb = 'Created' if created else 'Updated'
        self.stdout.write(self.style.SUCCESS(
            f'{verb} tenant {tenant.pk}: {tenant.name} ({tenant.slug})\n'
            f'  domain : {tenant.primary_domain}\n'
            f'  panel  : {tenant.cms_subdomain}\n'
            f'  master : {tenant.is_master}\n'
            f'  plan   : {tenant.plan}\n'
            f'  modules: {", ".join(enabled_now)}'
            + (f'\n  disabled {turned_off} module(s) not in the list' if turned_off else '')
        ))
        self.stdout.write(
            'No content was written. Add pages, footer links and settings in this '
            'panel, or let the fleet master push branding on the next check-in.'
        )
