from __future__ import annotations

import importlib.metadata
import json
import os
import platform
import re
import shutil
import socket
import sqlite3
import sys
import tempfile
import zipfile
from datetime import datetime, timezone
from pathlib import Path

from app.config import BASE_DIR, DB_PATH, LOG_DIR, SESSION_FILE, STORAGE_DIR, UPLOAD_DIR
from app.db import connect, get_bool, query_all, query_one, utcnow
from .instagram_service import InstagramService
from .lock_service import WorkerLock
from .scheduler_service import SchedulerService


class DebugService:
    def server_checks(self, is_https: bool = False) -> list[dict]:
        checks = []
        def add(name, ok, detail, solution=''):
            checks.append({'name': name, 'ok': bool(ok), 'detail': str(detail), 'solution': solution})
        add('Python Version', sys.version_info >= (3, 10), platform.python_version(), 'Use Python 3.10 or newer.')
        add('SQLite', True, sqlite3.sqlite_version)
        add('Storage Writable', os.access(STORAGE_DIR, os.W_OK), str(STORAGE_DIR), 'Check storage directory permissions.')
        add('Upload Directory Writable', os.access(UPLOAD_DIR, os.W_OK), str(UPLOAD_DIR), 'Check uploads directory permissions.')
        add('Project Path', BASE_DIR.exists(), str(BASE_DIR))
        add('Home Directory', Path.home().exists(), str(Path.home()))
        add('Python Executable', Path(sys.executable).exists(), sys.executable)
        add('SSL / HTTPS', is_https, 'HTTPS enabled' if is_https else 'HTTPS not detected', 'Enable SSL in cPanel before production use.')
        try:
            importlib.metadata.version('flask')
            importlib.metadata.version('instagrapi')
            add('Required Python Packages', True, 'Flask and instagrapi available')
        except importlib.metadata.PackageNotFoundError as exc:
            add('Required Python Packages', False, str(exc), 'Run pip install -r requirements.txt')
        try:
            with socket.create_connection(('www.instagram.com', 443), timeout=2):
                pass
            add('Internet Connection', True, 'instagram.com:443 reachable')
        except Exception as exc:
            add('Internet Connection', False, str(exc), 'Check outbound HTTPS access from hosting.')
        add('Cron Availability', True, 'Manual cPanel Cron is supported; UAPI=' + ('yes' if SchedulerService().cpanel_uapi_available() else 'no'))
        return checks

    def system_info(self, is_https: bool = False) -> dict:
        def version(name):
            try:
                return importlib.metadata.version(name)
            except importlib.metadata.PackageNotFoundError:
                return 'not installed'
        usage = shutil.disk_usage(STORAGE_DIR)
        return {
            'python_version': platform.python_version(),
            'flask_version': version('flask'),
            'instagrapi_version': version('instagrapi'),
            'sqlite_version': sqlite3.sqlite_version,
            'operating_system': platform.platform(),
            'project_path': str(BASE_DIR),
            'cwd': os.getcwd(),
            'python_executable': sys.executable,
            'home_directory': str(Path.home()),
            'storage_writable': os.access(STORAGE_DIR, os.W_OK),
            'uploads_writable': os.access(UPLOAD_DIR, os.W_OK),
            'database_writable': DB_PATH.exists() and os.access(DB_PATH, os.W_OK),
            'disk_free_bytes': usage.free,
            'server_time_utc': utcnow(),
            'timezone': str(datetime.now().astimezone().tzinfo),
            'https': is_https,
        }

    def database_info(self) -> dict:
        info = {'exists': DB_PATH.exists(), 'size_bytes': DB_PATH.stat().st_size if DB_PATH.exists() else 0, 'writable': DB_PATH.exists() and os.access(DB_PATH, os.W_OK)}
        try:
            conn = connect()
            info['tables_count'] = conn.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table'").fetchone()[0]
            info['integrity'] = conn.execute('PRAGMA integrity_check').fetchone()[0]
            conn.close()
        except Exception as exc:
            info['integrity'] = f'ERROR: {exc}'
            info['tables_count'] = 0
        return info

    def instagram_info(self) -> dict:
        row = query_one('SELECT * FROM instagram_accounts WHERE id=1')
        return {
            'session_file_exists': SESSION_FILE.exists(),
            'session_status': row['session_status'] if row else 'missing',
            'login_status': row['status'] if row else 'disconnected',
            'instagram_user_id': row['instagram_user_id'] if row else None,
            'username': row['username'] if row else None,
            'last_successful_request': row['last_successful_request_at'] if row else None,
            'last_error': row['last_error'] if row else None,
            'rate_limit': row['rate_limit_status'] if row else None,
            'challenge': row['challenge_status'] if row else None,
        }

    def self_diagnostics(self, is_https: bool = False, online_instagram: bool = False) -> list[dict]:
        checks = self.server_checks(is_https)
        db = self.database_info()
        checks.append({'name': 'Database Integrity', 'ok': db.get('integrity') == 'ok', 'detail': db.get('integrity'), 'solution': 'Run a backup and inspect SQLite integrity.'})
        checks.append({'name': 'Instagram Session', 'ok': SESSION_FILE.exists(), 'detail': str(SESSION_FILE), 'solution': 'Connect the Instagram account.'})
        if online_instagram and SESSION_FILE.exists():
            result = InstagramService().test_connection()
            checks.append({'name': 'Instagram Connection', 'ok': result['ok'], 'detail': result.get('message') or result.get('username'), 'solution': 'Reconnect the Instagram account if required.'})
        else:
            checks.append({'name': 'Instagram Connection', 'ok': True, 'detail': 'Online request skipped in diagnostics', 'solution': 'Use the dedicated connection test for a live request.'})
        lock = WorkerLock()
        checks.append({'name': 'Worker Lock', 'ok': not lock.path.exists() or lock._is_stale(), 'detail': str(lock.path), 'solution': 'Remove only a confirmed stale lock.'})
        sched = SchedulerService().status()
        checks.append({'name': 'Scheduler', 'ok': sched['status'] in {'running', 'not_configured'}, 'detail': sched['status'], 'solution': 'Check cPanel Cron if stopped.'})
        checks.append({'name': 'Media Storage', 'ok': os.access(UPLOAD_DIR, os.W_OK), 'detail': str(UPLOAD_DIR), 'solution': 'Fix uploads permissions.'})
        checks.append({'name': 'Automation Engine', 'ok': True, 'detail': 'Normalizer and database engine available', 'solution': ''})
        return checks

    def health(self) -> dict:
        sched = SchedulerService().status()
        db = self.database_info()
        ig = self.instagram_info()
        items = {
            'application': {'ok': True, 'detail': 'OK', 'solution': ''},
            'database': {'ok': db.get('integrity') == 'ok', 'detail': db.get('integrity'), 'solution': 'Run SQLite Integrity Check and restore a backup if required.'},
            'storage': {'ok': os.access(STORAGE_DIR, os.W_OK), 'detail': str(STORAGE_DIR), 'solution': 'Check storage directory permissions.'},
            'instagram': {'ok': ig['login_status'] == 'connected', 'detail': ig['login_status'], 'solution': 'Reconnect from the Instagram connection page.'},
            'session': {'ok': ig['session_file_exists'] and ig['session_status'] == 'valid', 'detail': ig['session_status'], 'solution': 'Create a fresh Instagram session by logging in again.'},
            'worker': {'ok': sched['status'] == 'running', 'detail': sched['status'], 'solution': 'Run the manual Worker test, then configure Cron.'},
            'cron': {'ok': sched['status'] == 'running', 'detail': sched['status'], 'solution': 'Open Scheduler and copy the cPanel Cron command.'},
            'uploads': {'ok': os.access(UPLOAD_DIR, os.W_OK), 'detail': str(UPLOAD_DIR), 'solution': 'Check uploads directory permissions.'},
            'debug': {'ok': not get_bool('debug_mode', False), 'detail': 'ON' if get_bool('debug_mode', False) else 'OFF', 'solution': 'Turn Debug Mode off in production.'},
        }
        failures = sum(1 for item in items.values() if not item['ok'])
        overall = 'healthy' if failures == 0 else ('warning' if failures <= 2 else 'critical')
        return {'overall': overall, 'items': items, 'scheduler': sched}

    _SENSITIVE_KEY = re.compile(r'(password|passwd|secret|session|cookie|authorization|token|csrf)', re.I)
    _SENSITIVE_VALUE = re.compile(
        r'(?i)(sessionid|csrftoken|authorization|password|passwd|token|secret)\s*[:=]\s*[^\s,;]+',
    )

    @classmethod
    def _scrub_debug_value(cls, value):
        if isinstance(value, dict):
            clean = {}
            for key, item in value.items():
                if cls._SENSITIVE_KEY.search(str(key)) and key not in {'session_status', 'session_file_exists'}:
                    clean[key] = '[REDACTED]'
                else:
                    clean[key] = cls._scrub_debug_value(item)
            return clean
        if isinstance(value, list):
            return [cls._scrub_debug_value(item) for item in value]
        if isinstance(value, tuple):
            return [cls._scrub_debug_value(item) for item in value]
        if isinstance(value, str):
            return cls._SENSITIVE_VALUE.sub(lambda m: m.group(1) + '=[REDACTED]', value)
        return value

    def create_debug_report(self) -> Path:
        stamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        path = STORAGE_DIR / f'debug_report_{stamp}.zip'
        report = {
            'system_info': self.system_info(False),
            'database': self.database_info(),
            'instagram': self.instagram_info(),
            'scheduler': SchedulerService().status(),
            'recent_errors': [dict(r) for r in query_all('SELECT id, error_type, module, action, message, created_at FROM error_logs ORDER BY id DESC LIMIT 50')],
            'recent_worker_runs': [dict(r) for r in query_all('SELECT * FROM worker_runs ORDER BY id DESC LIMIT 20')],
        }
        report = self._scrub_debug_value(report)
        with zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED) as zf:
            zf.writestr('debug_report.json', json.dumps(report, ensure_ascii=False, indent=2))
        try:
            path.chmod(0o600)
        except OSError:
            pass
        return path
