"""
Error Logger Module
Centralized error logging for Indian Stock Market Analysis System
Logs to both file (/tmp/stock_errors.log) and SQLite database
"""

import logging
import sqlite3
import traceback
import os
from datetime import datetime
from logging.handlers import RotatingFileHandler
from typing import Optional, Dict, Any

# Configuration
LOG_FILE = '/tmp/stock_errors.log'
DB_PATH = '/home/mcmarketshost/public_html/stock_analysis.db'
MAX_LOG_SIZE = 5 * 1024 * 1024  # 5 MB
BACKUP_COUNT = 3  # Keep 3 backup files

# Create logger
logger = logging.getLogger('stock_analysis')
logger.setLevel(logging.DEBUG)

# File handler with rotation
file_handler = RotatingFileHandler(
    LOG_FILE,
    maxBytes=MAX_LOG_SIZE,
    backupCount=BACKUP_COUNT
)
file_handler.setLevel(logging.DEBUG)

# Console handler for critical errors
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.ERROR)

# Formatter
formatter = logging.Formatter(
    '%(asctime)s | %(levelname)s | %(name)s | %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# Add handlers
if not logger.handlers:
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)


def init_error_table():
    """Create error_log table if it doesn't exist"""
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS error_log (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                level TEXT NOT NULL,
                source TEXT NOT NULL,
                symbol TEXT,
                error_type TEXT,
                message TEXT,
                traceback_info TEXT,
                extra_data TEXT
            )
        ''')
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_error_timestamp ON error_log(timestamp)')
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_error_source ON error_log(source)')
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_error_symbol ON error_log(symbol)')
        conn.commit()
        conn.close()
        return True
    except Exception as e:
        logger.error("Failed to init error_log table: {}".format(str(e)))
        return False


def log_to_db(level, source, message, symbol=None, error_type=None, traceback_info=None, extra_data=None):
    """Log error to SQLite database"""
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute('''
            INSERT INTO error_log (timestamp, level, source, symbol, error_type, message, traceback_info, extra_data)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            datetime.now().isoformat(),
            level,
            source,
            symbol,
            error_type,
            message[:2000] if message else None,  # Limit message length
            traceback_info[:5000] if traceback_info else None,  # Limit traceback length
            extra_data[:2000] if extra_data else None
        ))
        conn.commit()
        conn.close()
        return True
    except Exception as e:
        logger.error("Failed to log to DB: {}".format(str(e)))
        return False


def log_error(source, message, symbol=None, exception=None, extra_data=None):
    """
    Log an error to both file and database

    Args:
        source: Module/function name (e.g., 'scraper_nse.get_delivery_data')
        message: Error description
        symbol: Stock symbol if applicable
        exception: Exception object if available
        extra_data: Additional context as string
    """
    error_type = type(exception).__name__ if exception else 'Error'
    tb = traceback.format_exc() if exception else None

    # Log to file
    log_msg = "[{}] {} | Symbol: {} | {}".format(
        source,
        message,
        symbol or 'N/A',
        str(exception) if exception else ''
    )
    logger.error(log_msg)

    # Log to database
    log_to_db(
        level='ERROR',
        source=source,
        message=message,
        symbol=symbol,
        error_type=error_type,
        traceback_info=tb,
        extra_data=extra_data
    )


def log_warning(source, message, symbol=None, extra_data=None):
    """Log a warning"""
    log_msg = "[{}] {} | Symbol: {}".format(source, message, symbol or 'N/A')
    logger.warning(log_msg)

    log_to_db(
        level='WARNING',
        source=source,
        message=message,
        symbol=symbol,
        extra_data=extra_data
    )


def log_info(source, message, symbol=None):
    """Log info message (file only, not to DB to avoid bloat)"""
    log_msg = "[{}] {} | Symbol: {}".format(source, message, symbol or 'N/A')
    logger.info(log_msg)


def log_debug(source, message, symbol=None):
    """Log debug message (file only)"""
    log_msg = "[{}] {} | Symbol: {}".format(source, message, symbol or 'N/A')
    logger.debug(log_msg)


def log_scraper_error(scraper_name, function_name, symbol, exception, response_code=None):
    """
    Convenience function for scraper errors

    Args:
        scraper_name: e.g., 'scraper_nse', 'scraper_yahoo'
        function_name: e.g., 'get_delivery_data', 'get_quick_quote'
        symbol: Stock symbol
        exception: The exception that was raised
        response_code: HTTP response code if applicable
    """
    source = "{}.{}".format(scraper_name, function_name)
    extra = "HTTP {}".format(response_code) if response_code else None
    log_error(source, str(exception), symbol=symbol, exception=exception, extra_data=extra)


def log_claude_error(symbol, error_type, message, raw_response=None):
    """
    Log Claude CLI errors

    Args:
        symbol: Stock symbol being analyzed
        error_type: 'timeout', 'parse_error', 'api_error', etc.
        message: Error description
        raw_response: Raw Claude response if available
    """
    source = "claude_cli"
    extra = raw_response[:500] if raw_response else None
    log_to_db(
        level='ERROR',
        source=source,
        message=message,
        symbol=symbol,
        error_type=error_type,
        extra_data=extra
    )
    logger.error("[claude_cli] {} | Symbol: {} | {}".format(error_type, symbol, message))


def get_recent_errors(limit=50, source=None, symbol=None):
    """
    Get recent errors from database

    Args:
        limit: Number of errors to return
        source: Filter by source
        symbol: Filter by symbol

    Returns:
        List of error dictionaries
    """
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.row_factory = sqlite3.Row
        cursor = conn.cursor()

        query = 'SELECT * FROM error_log WHERE 1=1'
        params = []

        if source:
            query += ' AND source LIKE ?'
            params.append('%{}%'.format(source))

        if symbol:
            query += ' AND symbol = ?'
            params.append(symbol)

        query += ' ORDER BY id DESC LIMIT ?'
        params.append(limit)

        cursor.execute(query, params)
        rows = cursor.fetchall()
        conn.close()

        return [dict(row) for row in rows]
    except Exception as e:
        logger.error("Failed to get recent errors: {}".format(str(e)))
        return []


def get_error_stats(hours=24):
    """
    Get error statistics for the last N hours

    Args:
        hours: Number of hours to look back

    Returns:
        Dictionary with error counts by source
    """
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()

        cursor.execute('''
            SELECT source, COUNT(*) as count, level
            FROM error_log
            WHERE timestamp > datetime('now', '-{} hours')
            GROUP BY source, level
            ORDER BY count DESC
        '''.format(hours))

        rows = cursor.fetchall()
        conn.close()

        stats = {}
        for source, count, level in rows:
            if source not in stats:
                stats[source] = {'total': 0, 'errors': 0, 'warnings': 0}
            stats[source]['total'] += count
            if level == 'ERROR':
                stats[source]['errors'] = count
            elif level == 'WARNING':
                stats[source]['warnings'] = count

        return stats
    except Exception as e:
        logger.error("Failed to get error stats: {}".format(str(e)))
        return {}


def cleanup_old_errors(days=30):
    """
    Delete errors older than N days

    Args:
        days: Number of days to keep

    Returns:
        Number of deleted rows
    """
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute('''
            DELETE FROM error_log
            WHERE timestamp < datetime('now', '-{} days')
        '''.format(days))
        deleted = cursor.rowcount
        conn.commit()
        conn.close()
        logger.info("Cleaned up {} old error log entries".format(deleted))
        return deleted
    except Exception as e:
        logger.error("Failed to cleanup old errors: {}".format(str(e)))
        return 0


# Initialize error table on module import
init_error_table()


if __name__ == '__main__':
    # Test the logger
    print("Testing error logger...")

    # Test logging
    log_info('test', 'This is an info message')
    log_warning('test', 'This is a warning', symbol='RELIANCE')
    log_error('test.function', 'This is an error', symbol='TCS', exception=ValueError("Test error"))
    log_scraper_error('scraper_nse', 'get_delivery_data', 'INFY', Exception("HTTP 404"), response_code=404)
    log_claude_error('HDFC', 'timeout', 'Claude CLI timed out after 60s')

    print("\nRecent errors:")
    errors = get_recent_errors(limit=5)
    for e in errors:
        print("  {} | {} | {} | {}".format(e['timestamp'], e['source'], e['symbol'], e['message']))

    print("\nError stats (24h):")
    stats = get_error_stats()
    for source, data in stats.items():
        print("  {}: {} errors, {} warnings".format(source, data['errors'], data['warnings']))

    print("\nLog file: {}".format(LOG_FILE))
    print("Done!")
