"""
Automation Logger Service
Handles file-based logging for automation runs
"""
import os
import logging
from datetime import datetime
from pathlib import Path
from typing import List
import json

logger = logging.getLogger(__name__)


class AutomationLogger:
    """File-based logging for automation runs

    Writes logs under a per-account/per-site/run directory by default.
    Optionally a shared_log_dir can be provided to mirror logs into a consolidated folder.
    """

    def __init__(self, base_log_dir: str = '/data/app/logs/automation', shared_log_dir: str | None = None):
        # Use absolute path by default to avoid surprises from current working directory
        self.base_log_dir = base_log_dir
        self.shared_log_dir = shared_log_dir
    
    def start_run(self, account_id: int, site_id: int, trigger_type: str) -> str:
        """
        Create log directory structure and return run_id
        
        Returns:
            run_id in format: run_20251203_140523_manual
        """
        # Generate run_id
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        run_id = f"run_{timestamp}_{trigger_type}"
        
        # Create directory structure (primary)
        run_dir = self._get_run_dir(account_id, site_id, run_id)
        os.makedirs(run_dir, exist_ok=True)

        # Create mirrored directory in shared log dir if configured
        shared_run_dir = None
        if self.shared_log_dir:
            shared_run_dir = os.path.join(self.shared_log_dir, run_id)
            os.makedirs(shared_run_dir, exist_ok=True)
        
        # Create main log file in primary run dir
        log_file = os.path.join(run_dir, 'automation_run.log')
        with open(log_file, 'w') as f:
            f.write("=" * 80 + "\n")
            f.write(f"AUTOMATION RUN: {run_id}\n")
            f.write(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"Trigger: {trigger_type}\n")
            f.write(f"Account: {account_id}\n")
            f.write(f"Site: {site_id}\n")
            f.write("=" * 80 + "\n\n")

        # Also create a main log in the shared run dir (if configured)
        if shared_run_dir:
            shared_log_file = os.path.join(shared_run_dir, 'automation_run.log')
            with open(shared_log_file, 'w') as f:
                f.write("=" * 80 + "\n")
                f.write(f"AUTOMATION RUN (SHARED): {run_id}\n")
                f.write(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"Trigger: {trigger_type}\n")
                f.write(f"Account: {account_id}\n")
                f.write(f"Site: {site_id}\n")
                f.write("=" * 80 + "\n\n")
        # Structured trace event for run start
        try:
            trace_event = {
                'event': 'run_started',
                'run_id': run_id,
                'trigger': trigger_type,
                'account_id': account_id,
                'site_id': site_id,
                'timestamp': datetime.now().isoformat(),
            }
            # best-effort append
            run_dir = self._get_run_dir(account_id, site_id, run_id)
            os.makedirs(run_dir, exist_ok=True)
            trace_file = os.path.join(run_dir, 'run_trace.jsonl')
            with open(trace_file, 'a') as tf:
                tf.write(json.dumps(trace_event) + "\n")
            if self.shared_log_dir:
                shared_trace = os.path.join(self.shared_log_dir, run_id, 'run_trace.jsonl')
                os.makedirs(os.path.dirname(shared_trace), exist_ok=True)
                with open(shared_trace, 'a') as stf:
                    stf.write(json.dumps(trace_event) + "\n")
        except Exception:
            pass
        
        logger.info(f"[AutomationLogger] Created run: {run_id}")
        return run_id
    
    def log_stage_start(self, run_id: str, account_id: int, site_id: int, stage_number: int, stage_name: str, pending_count: int):
        """Log stage start"""
        timestamp = self._timestamp()
        
        # Main log
        self._append_to_main_log(account_id, site_id, run_id, 
                                 f"{timestamp} - Stage {stage_number} starting: {stage_name}")
        self._append_to_main_log(account_id, site_id, run_id, 
                                 f"{timestamp} - Stage {stage_number}: Found {pending_count} pending items")
        
        # Stage-specific log (primary)
        stage_log = self._get_stage_log_path(account_id, site_id, run_id, stage_number)
        os.makedirs(os.path.dirname(stage_log), exist_ok=True)
        with open(stage_log, 'w') as f:
            f.write("=" * 80 + "\n")
            f.write(f"STAGE {stage_number}: {stage_name}\n")
            f.write(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write("=" * 80 + "\n\n")
            f.write(f"{timestamp} - Found {pending_count} pending items\n")

        # Mirror stage log into shared dir if configured
        if self.shared_log_dir:
            shared_stage_log = os.path.join(self.shared_log_dir, run_id, f'stage_{str(stage_number)}.log')
            os.makedirs(os.path.dirname(shared_stage_log), exist_ok=True)
            with open(shared_stage_log, 'w') as f:
                f.write("=" * 80 + "\n")
                f.write(f"STAGE {stage_number}: {stage_name} (SHARED)\n")
                f.write(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write("=" * 80 + "\n\n")
                f.write(f"{timestamp} - Found {pending_count} pending items\n")
        # Structured stage start trace
        try:
            trace_event = {
                'event': 'stage_start',
                'run_id': run_id,
                'stage': stage_number,
                'stage_name': stage_name,
                'pending_count': pending_count,
                'timestamp': datetime.now().isoformat(),
            }
            run_dir = self._get_run_dir(account_id, site_id, run_id)
            os.makedirs(run_dir, exist_ok=True)
            trace_file = os.path.join(run_dir, 'run_trace.jsonl')
            with open(trace_file, 'a') as tf:
                tf.write(json.dumps(trace_event) + "\n")
            if self.shared_log_dir:
                shared_trace = os.path.join(self.shared_log_dir, run_id, 'run_trace.jsonl')
                os.makedirs(os.path.dirname(shared_trace), exist_ok=True)
                with open(shared_trace, 'a') as stf:
                    stf.write(json.dumps(trace_event) + "\n")
        except Exception:
            pass
    
    def log_stage_progress(self, run_id: str, account_id: int, site_id: int, stage_number: int, message: str):
        """Log stage progress"""
        timestamp = self._timestamp()
        log_message = f"{timestamp} - Stage {stage_number}: {message}"
        
        # Main log
        self._append_to_main_log(account_id, site_id, run_id, log_message)
        
        # Stage-specific log (primary)
        stage_log = self._get_stage_log_path(account_id, site_id, run_id, stage_number)
        os.makedirs(os.path.dirname(stage_log), exist_ok=True)
        with open(stage_log, 'a') as f:
            f.write(f"{log_message}\n")

        # Mirror progress into shared dir if configured
        if self.shared_log_dir:
            shared_stage_log = os.path.join(self.shared_log_dir, run_id, f'stage_{str(stage_number)}.log')
            os.makedirs(os.path.dirname(shared_stage_log), exist_ok=True)
            with open(shared_stage_log, 'a') as f:
                f.write(f"{log_message}\n")
        # Structured progress trace
        try:
            trace_event = {
                'event': 'stage_progress',
                'run_id': run_id,
                'stage': stage_number,
                'message': message,
                'timestamp': datetime.now().isoformat(),
            }
            run_dir = self._get_run_dir(account_id, site_id, run_id)
            os.makedirs(run_dir, exist_ok=True)
            trace_file = os.path.join(run_dir, 'run_trace.jsonl')
            with open(trace_file, 'a') as tf:
                tf.write(json.dumps(trace_event) + "\n")
            if self.shared_log_dir:
                shared_trace = os.path.join(self.shared_log_dir, run_id, 'run_trace.jsonl')
                os.makedirs(os.path.dirname(shared_trace), exist_ok=True)
                with open(shared_trace, 'a') as stf:
                    stf.write(json.dumps(trace_event) + "\n")
        except Exception:
            pass
    
    def log_stage_complete(self, run_id: str, account_id: int, site_id: int, stage_number: int, 
                          processed_count: int, time_elapsed: str, credits_used: int):
        """Log stage completion"""
        timestamp = self._timestamp()
        
        # Main log
        self._append_to_main_log(account_id, site_id, run_id, 
                                 f"{timestamp} - Stage {stage_number} complete: {processed_count} items processed")
        
        # Stage-specific log (primary)
        stage_log = self._get_stage_log_path(account_id, site_id, run_id, stage_number)
        os.makedirs(os.path.dirname(stage_log), exist_ok=True)
        with open(stage_log, 'a') as f:
            f.write("\n" + "=" * 80 + "\n")
            f.write(f"STAGE {stage_number} COMPLETE\n")
            f.write(f"Total Time: {time_elapsed}\n")
            f.write(f"Processed: {processed_count} items\n")
            f.write(f"Credits Used: {credits_used}\n")
            f.write("=" * 80 + "\n")

        # Mirror completion into shared dir if configured
        if self.shared_log_dir:
            shared_stage_log = os.path.join(self.shared_log_dir, run_id, f'stage_{str(stage_number)}.log')
            os.makedirs(os.path.dirname(shared_stage_log), exist_ok=True)
            with open(shared_stage_log, 'a') as f:
                f.write("\n" + "=" * 80 + "\n")
                f.write(f"STAGE {stage_number} COMPLETE (SHARED)\n")
                f.write(f"Total Time: {time_elapsed}\n")
                f.write(f"Processed: {processed_count} items\n")
                f.write(f"Credits Used: {credits_used}\n")
                f.write("=" * 80 + "\n")
        # Structured completion trace
        try:
            trace_event = {
                'event': 'stage_complete',
                'run_id': run_id,
                'stage': stage_number,
                'processed_count': processed_count,
                'time_elapsed': time_elapsed,
                'credits_used': credits_used,
                'timestamp': datetime.now().isoformat(),
            }
            run_dir = self._get_run_dir(account_id, site_id, run_id)
            os.makedirs(run_dir, exist_ok=True)
            trace_file = os.path.join(run_dir, 'run_trace.jsonl')
            with open(trace_file, 'a') as tf:
                tf.write(json.dumps(trace_event) + "\n")
            if self.shared_log_dir:
                shared_trace = os.path.join(self.shared_log_dir, run_id, 'run_trace.jsonl')
                os.makedirs(os.path.dirname(shared_trace), exist_ok=True)
                with open(shared_trace, 'a') as stf:
                    stf.write(json.dumps(trace_event) + "\n")
        except Exception:
            pass
    
    def log_stage_error(self, run_id: str, account_id: int, site_id: int, stage_number: int, error_message: str):
        """Log stage error"""
        timestamp = self._timestamp()
        log_message = f"{timestamp} - Stage {stage_number} ERROR: {error_message}"
        
        # Main log
        self._append_to_main_log(account_id, site_id, run_id, log_message)
        
        # Stage-specific log (primary)
        stage_log = self._get_stage_log_path(account_id, site_id, run_id, stage_number)
        os.makedirs(os.path.dirname(stage_log), exist_ok=True)
        with open(stage_log, 'a') as f:
            f.write(f"\n{log_message}\n")

        # Mirror error into shared dir if configured
        if self.shared_log_dir:
            shared_stage_log = os.path.join(self.shared_log_dir, run_id, f'stage_{str(stage_number)}.log')
            os.makedirs(os.path.dirname(shared_stage_log), exist_ok=True)
            with open(shared_stage_log, 'a') as f:
                f.write(f"\n{log_message}\n")
        # Structured error trace
        try:
            trace_event = {
                'event': 'stage_error',
                'run_id': run_id,
                'stage': stage_number,
                'error': error_message,
                'timestamp': datetime.now().isoformat(),
            }
            run_dir = self._get_run_dir(account_id, site_id, run_id)
            os.makedirs(run_dir, exist_ok=True)
            trace_file = os.path.join(run_dir, 'run_trace.jsonl')
            with open(trace_file, 'a') as tf:
                tf.write(json.dumps(trace_event) + "\n")
            if self.shared_log_dir:
                shared_trace = os.path.join(self.shared_log_dir, run_id, 'run_trace.jsonl')
                os.makedirs(os.path.dirname(shared_trace), exist_ok=True)
                with open(shared_trace, 'a') as stf:
                    stf.write(json.dumps(trace_event) + "\n")
        except Exception:
            pass
    
    def get_activity_log(self, account_id: int, site_id: int, run_id: str, last_n: int = 50) -> List[str]:
        """
        Get last N lines from main activity log
        
        Returns:
            List of log lines (newest first)
        """
        log_file = os.path.join(self._get_run_dir(account_id, site_id, str(run_id)), 'automation_run.log')
        
        if not os.path.exists(log_file):
            return []
        
        with open(log_file, 'r') as f:
            lines = f.readlines()
        
        # Filter out header lines and empty lines
        activity_lines = [line.strip() for line in lines if line.strip() and not line.startswith('=')]
        
        # Return last N lines (newest first)
        return list(reversed(activity_lines[-last_n:]))
    
    # Helper methods
    
    def _get_run_dir(self, account_id: int, site_id: int, run_id: str) -> str:
        """Get run directory path"""
        return os.path.join(self.base_log_dir, str(account_id), str(site_id), run_id)
    
    def _get_stage_log_path(self, account_id: int, site_id: int, run_id: str, stage_number: int) -> str:
        """Get stage log file path"""
        run_dir = self._get_run_dir(account_id, site_id, run_id)
        return os.path.join(run_dir, f'stage_{str(stage_number)}.log')
    
    def _append_to_main_log(self, account_id: int, site_id: int, run_id: str, message: str):
        """Append message to main log file"""
        # Ensure base log dir exists
        try:
            os.makedirs(self.base_log_dir, exist_ok=True)
        except Exception:
            # Best-effort: if directory creation fails, still attempt to write to run dir
            pass

        log_file = os.path.join(self._get_run_dir(account_id, site_id, run_id), 'automation_run.log')
        os.makedirs(os.path.dirname(log_file), exist_ok=True)
        with open(log_file, 'a') as f:
            f.write(f"{message}\n")

        # Also append to a diagnostic file so we can trace logger calls across runs
        try:
            diag_file = os.path.join(self.base_log_dir, 'automation_diagnostic.log')
            with open(diag_file, 'a') as df:
                df.write(f"{self._timestamp()} - {account_id}/{site_id}/{run_id} - {message}\n")
        except Exception:
            # Never fail the main logging flow because of diagnostics
            pass

    def append_trace(self, account_id: int, site_id: int, run_id: str, event: dict):
        """Public helper to append a structured trace event (JSONL) for a run and mirror to shared dir."""
        try:
            run_dir = self._get_run_dir(account_id, site_id, run_id)
            os.makedirs(run_dir, exist_ok=True)
            trace_file = os.path.join(run_dir, 'run_trace.jsonl')
            with open(trace_file, 'a') as tf:
                tf.write(json.dumps(event) + "\n")
        except Exception:
            # Best-effort: ignore trace write failures
            pass

        if self.shared_log_dir:
            try:
                shared_run_dir = os.path.join(self.shared_log_dir, run_id)
                os.makedirs(shared_run_dir, exist_ok=True)
                shared_trace = os.path.join(shared_run_dir, 'run_trace.jsonl')
                with open(shared_trace, 'a') as stf:
                    stf.write(json.dumps(event) + "\n")
            except Exception:
                pass
    
    def _timestamp(self) -> str:
        """Get formatted timestamp"""
        return datetime.now().strftime('%H:%M:%S')
