
from urllib.parse import parse_qs, urlencode
from fastapi import Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from sqlalchemy.orm import Session
from app.db.models import OperationLog,User
from app.core.apiexception import  APIException
from app.core.dbmanager import get_log_db
from app.db.crud import create_log
import json

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        if request.method in ("POST", "PUT", "PATCH","DELETE"):
            content_type = request.headers.get('content-type', '')
            if content_type.startswith('multipart/form-data'):
                request.state.body = None  
            else:
                try:
                    request.state.body = await request.json()
                except json.JSONDecodeError:
                    request.state.body = await request.body()
        else:
            request.state.body = None

        try:
            response = await call_next(request)
            state = request.state 
        except Exception as e:
            await self.log_error(request, e)
            response = JSONResponse(
                content={"detail": "Internal Server Error"},
                status_code=500
            )
        
        if hasattr(request.state, "user") and hasattr(request.state, "tenant"):
            await self.log_request(request, response,state)

        return response
    
    def sanitize_password(self,data):
        """
        データ内の password パラメータをフィルタリングする機能。
        dict、JSON 文字列、URL エンコード文字列、QueryDict をサポート。
        """
        if data is None:
            return data
        elif isinstance(data, dict):
            data.pop('password', None)
            return data
        elif isinstance(data, list):
            return [self.sanitize_password(item) for item in data]
        elif isinstance(data, (str, bytes)):
            if isinstance(data, bytes):
                data = data.decode('utf-8')  # bytes to str
            # JSON解析
            try:
                parsed_json = json.loads(data)
                sanitized_json = self.sanitize_password(parsed_json)
                return json.dumps(sanitized_json, separators=(',', ':'))
            except json.JSONDecodeError:
                # URL 解析
                try:
                    parsed_dict = parse_qs(data)
                    parsed_dict.pop('password', None)
                    return urlencode(parsed_dict, doseq=True)
                except:
                    parts = data.split('&')
                    filtered_parts = []
                    for part in parts:
                        if '=' in part:
                            key, _ = part.split('=', 1)
                            if key == 'password':
                                continue
                        filtered_parts.append(part)
                    return '&'.join(filtered_parts)
        #  QueryDict 例えば FastAPI の request.query_params）
        elif hasattr(data, 'items'): 
            return {k: v for k, v in data.items() if k != 'password'}
        return data


    async def log_request(self, request: Request, response,state):
        try:
            headers = dict(request.headers)
            route = request.scope.get("route")
            if route:
                path_template = route.path
            else:
                path_template = request.url.path
          
            # passwordのパラメータを除外する
            safe_query = self.sanitize_password(request.query_params.items())
               
            # passwordのパラメータを除外する
            safe_body = self.sanitize_password(request.state.body)
           
            db_operation = OperationLog(
                tenantid =request.state.tenant,
                clientip = request.client.host if request.client else None,
                useragent =headers.get("user-agent", ""),
                userid = request.state.user,
                operation = request.method,
                function = path_template,
                parameters = str({
                    "path": request.path_params,
                    "query": safe_query,
                    "body": safe_body
                }),
                response = f"status_code:{response.status_code }"  )
            
            db = request.state.db
            if db:
                await self.write_log_to_db(db_operation,db)  
          
        except Exception as e:
            print(f"Logging failed: {str(e)}")


    async def log_error(self, request: Request, e: Exception):
        exc =  APIException('operation:dispatch',request.url._url,f"Error occurred while writting operation log:",e)
        db = get_log_db()
        try:
            create_log(db,exc.error)
        finally:
            db.close()

    async def write_log_to_db(self, db_operation,db):
        db.add(db_operation)
        db.commit()
