import base64
import logging
import uuid
from datetime import datetime

import requests
from scrapy import signals
from scrapy.http import Request

logger = logging.getLogger(__name__)

class SessionTrackerMiddleware:
    DEFAULT_BASE_URL = "http://127.0.0.1:8765"
    
    def __init__(self, base_url=None):
        self.base_url = (base_url or self.DEFAULT_BASE_URL).rstrip('/')
        self.session = requests.Session()
    
    @classmethod
    def from_crawler(cls, crawler):
        base_url = crawler.settings.get('SESSION_STORE_URL', cls.DEFAULT_BASE_URL) if crawler.settings else cls.DEFAULT_BASE_URL
        middleware = cls(base_url=base_url)
        crawler.signals.connect(middleware.spider_opened, signal=signals.spider_opened)
        return middleware
    
    def spider_opened(self, spider):
        logger.info(f"Cleaning request storage for spider: {spider.name}")
        try:
            self.session.delete(f"{self.base_url}/clean").raise_for_status()
        except Exception as e:
            logger.error(f"Failed to clean storage: {e}")
    
    async def process_start(self, start):
        async for request in start:
            request.meta['_id'] = str(uuid.uuid4())
            request.meta['_start_time'] = datetime.utcnow()
            request.meta['_parent_id'] = None
            yield request
    
    def process_spider_input(self, response, spider):
        req_id = response.meta.get('_id')
        if not req_id:
            return None
        try:
            start_time = response.meta.get('_start_time')
            duration_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000) if start_time else 0
            data = {
                'id': req_id,
                'parent_id': response.meta.get('_parent_id'),
                'timestamp': start_time.isoformat() if start_time else datetime.utcnow().isoformat(),
                'duration_ms': duration_ms,
                'request': {
                    'method': response.request.method,
                    'url': response.request.url,
                    'headers': dict(response.request.headers.to_unicode_dict()),
                    'body': response.request.body.decode('utf-8') if response.request.body else None,
                },
                'response': {
                    'status': response.status,
                    'headers': dict(response.headers.to_unicode_dict()),
                    'body': response.text,
                    'encoding': response.encoding,
                    'flags': response.flags,
                },
                'exception': None
            }
            for key in ['request', 'response']:
                if data.get(key, {}).get('body'):
                    body = data[key]['body']
                    if isinstance(body, str):
                        body = body.encode('utf-8')
                    data[key]['body'] = base64.b64encode(body).decode('ascii')
            resp = self.session.post(f"{self.base_url}/requests", json=data, timeout=5)
            resp.raise_for_status()
        except Exception as e:
            logger.error(f"Failed to store request {req_id}: {e}")
        return None
    
    async def process_spider_output(self, response, result, spider):
        async for item in result:
            if isinstance(item, Request):
                item.meta['_id'] = str(uuid.uuid4())
                item.meta['_start_time'] = datetime.utcnow()
                parent_id = response.meta.get('_id')
                if parent_id:
                    item.meta['_parent_id'] = parent_id
            yield item
    
