"""
Management command that streams logs from stern and ingests them continuously.

Usage:
  python manage.py nginxmon_stern
  python manage.py nginxmon_stern --namespace ingress-nginx --selector app.kubernetes.io/name=ingress-nginx
  python manage.py nginxmon_stern --tail 200 --batch-size 20

The command checks NginxSettings.enabled every few seconds and pauses/resumes
ingestion accordingly. Stop with Ctrl-C.
"""
import subprocess
import time
import signal
import sys

from django.core.management.base import BaseCommand

from nginxmon.fetcher import ingest_raw
from nginxmon.models import NginxSettings


_STERN_BIN = '/opt/homebrew/bin/stern'
_DEFAULT_NAMESPACE = 'ingress-nginx'
_DEFAULT_SELECTOR = 'ingress-nginx-controller'
_BATCH_SIZE = 30
_ENABLED_CHECK_INTERVAL = 5  # seconds between enabled-flag checks
_FLUSH_INTERVAL = 3           # seconds before flushing a partial batch


class Command(BaseCommand):
    help = 'Stream nginx logs via stern and continuously ingest them into the database'

    def add_arguments(self, parser):
        parser.add_argument('--namespace', '-n', default=_DEFAULT_NAMESPACE,
                            help='Kubernetes namespace (default: ingress-nginx)')
        parser.add_argument('--selector', '-l', default=_DEFAULT_SELECTOR,
                            help='Pod selector passed to stern (default: ingress-nginx-controller)')
        parser.add_argument('--tail', type=int, default=500,
                            help='Number of historical lines to retrieve on start (default: 500)')
        parser.add_argument('--batch-size', type=int, default=_BATCH_SIZE,
                            help='Number of lines to batch before ingesting (default: 30)')

    def handle(self, *args, **options):
        namespace = options['namespace']
        selector = options['selector']
        tail = options['tail']
        batch_size = options['batch_size']

        self._shutdown = False
        signal.signal(signal.SIGINT, self._handle_signal)
        signal.signal(signal.SIGTERM, self._handle_signal)

        self.stdout.write(self.style.SUCCESS(
            f'Starting stern: selector={selector!r} namespace={namespace!r} tail={tail}'
        ))

        cmd = [
            _STERN_BIN,
            selector,
            '--namespace', namespace,
            '--tail', str(tail),
            '--color', 'never',
            '--template', '{{.PodName}} {{.ContainerName}} {{.Message}}\n',
        ]

        self.stdout.write(f'Command: {" ".join(cmd)}')

        proc = None
        try:
            proc = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=None,   # pass stern's stderr straight to the terminal
                text=True,
                bufsize=1,
            )
            self._ingest_loop(proc, batch_size)
        except FileNotFoundError:
            self.stderr.write(self.style.ERROR(
                f'stern not found at {_STERN_BIN}. Install with: brew install stern'
            ))
            sys.exit(1)
        finally:
            if proc and proc.poll() is None:
                proc.terminate()
                try:
                    proc.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    proc.kill()

    def _handle_signal(self, signum, frame):
        self.stdout.write('\nShutting down stern ingestion…')
        self._shutdown = True

    def _ingest_loop(self, proc, batch_size):
        batch = []
        last_flush = time.monotonic()
        last_enabled_check = 0.0
        enabled = True

        import select

        while not self._shutdown:
            # Periodically re-check NginxSettings.enabled
            now = time.monotonic()
            if now - last_enabled_check >= _ENABLED_CHECK_INTERVAL:
                try:
                    enabled = NginxSettings.get().enabled
                except Exception:
                    pass
                last_enabled_check = now

            if proc.poll() is not None:
                # stern process exited
                self.stderr.write(self.style.WARNING('stern process exited.'))
                break

            # Non-blocking readline with a short select timeout
            ready, _, _ = select.select([proc.stdout], [], [], 0.5)
            if ready:
                line = proc.stdout.readline()
                if not line:
                    break
                if enabled:
                    batch.append(line)

            # Flush when batch is full or flush interval elapsed
            if batch and (len(batch) >= batch_size or time.monotonic() - last_flush >= _FLUSH_INTERVAL):
                self._flush(batch)
                batch = []
                last_flush = time.monotonic()

        # Final flush
        if batch:
            self._flush(batch)

    def _flush(self, lines):
        text = ''.join(lines)
        try:
            count = ingest_raw(text)
            if count:
                self.stdout.write(f'  [stern] Ingested {count} new log entries')
        except Exception as exc:
            self.stderr.write(self.style.ERROR(f'  [stern] Ingest error: {exc}'))
