from typing import Dict, Any, List


class FormatAdapter:
    """Convertit le JSON OCR brut en format normalisé PageData.

    Supporte quatre formats d'entrée :
      - Format A (PaddleOCR legacy) : { job_id, pages: [{ page: { text_blocks } }] }
      - Format B (Finspot/simple)   : { uuid, ocr_data: { pages: [{ words }] } }
      - Format C (Elliott / Futur stockage minio)  : { correlation_id, pages: [{ ocr_data: { boxes } }] }
      - Format D (Flat / single page)              : { doc_name, boxes: [...], width_page, height_page }
    """

    @staticmethod
    def convert_bbox(bbox: Dict[str, float]) -> List[int]:
        x_coords = [bbox['x1'], bbox['x2'], bbox['x3'], bbox['x4']]
        y_coords = [bbox['y1'], bbox['y2'], bbox['y3'], bbox['y4']]
        return [int(min(x_coords)), int(min(y_coords)), int(max(x_coords)), int(max(y_coords))]

    @staticmethod
    def is_raw_format(data: Dict[str, Any]) -> bool:
        """Détecte si le JSON est dans un format brut nécessitant conversion."""
        # Format A : PaddleOCR legacy
        if "pages" in data and "job_id" in data:
            return True
        # Format B : Finspot / simple OCR (uuid + ocr_data.pages[].words[])
        if "ocr_data" in data and "pages" in data.get("ocr_data", {}):
            return True
        # Format C : Cifacile / broker (correlation_id + pages[].ocr_data.boxes[])
        if "correlation_id" in data and "pages" in data:
            return True
        # Format D : Flat single-page (boxes[] au top-level, pas de pages[])
        if "boxes" in data and "pages" not in data:
            return True
        return False

    @staticmethod
    def convert_to_normalized_format(raw_data: Dict[str, Any]) -> Dict[str, Any]:
        # Format C : correlation_id + pages[].ocr_data.boxes[]
        if "correlation_id" in raw_data and "pages" in raw_data:
            return FormatAdapter._convert_broker_format(raw_data)
        # Format D : flat single-page (boxes[] au top-level, pas de pages[])
        if "boxes" in raw_data and "pages" not in raw_data:
            return FormatAdapter._convert_flat_format(raw_data)
        # Format B : ocr_data.pages[].words[]
        if "ocr_data" in raw_data:
            return FormatAdapter._convert_simple_format(raw_data)
        # Format A : legacy PaddleOCR
        return FormatAdapter._convert_legacy_format(raw_data)

    @staticmethod
    def _convert_simple_format(raw_data: Dict[str, Any]) -> Dict[str, Any]:
        """Convertit le format simple (uuid + ocr_data.pages[].words[])."""
        document_id = raw_data.get("uuid", "unknown")
        doc_name = raw_data.get("filename", "unknown.pdf")
        normalized_pages = []

        for page_data in raw_data.get("ocr_data", {}).get("pages", []):
            page_number = page_data.get("page_number", 1)
            words = page_data.get("words", [])

            if words:
                all_x = [b["bbox"][2] for b in words if "bbox" in b]
                all_y = [b["bbox"][3] for b in words if "bbox" in b]
                width = max(all_x) if all_x else 1700
                height = max(all_y) if all_y else 2200
            else:
                width, height = 1700, 2200

            text_blocks = []
            for w in words:
                bbox = w.get("bbox", [0, 0, 0, 0])
                text_blocks.append({
                    "text": w.get("text", ""),
                    "bbox": [int(b) for b in bbox],
                    "confidence": w.get("confidence", 0.0),
                })

            normalized_pages.append({
                "document_id": document_id,
                "page_index": page_number,
                "source": "paddleocr",
                "width": width,
                "height": height,
                "doc_name": doc_name,
                "text_blocks": text_blocks,
                "metadata": {},
            })

        return {
            "document_id": document_id,
            "normalized_pages_data": normalized_pages,
        }

    @staticmethod
    def _convert_legacy_format(raw_data: Dict[str, Any]) -> Dict[str, Any]:
        """Convertit le format legacy PaddleOCR (job_id + pages[].page.text_blocks[])."""
        document_id = raw_data.get("job_id", "unknown")
        normalized_pages = []

        for page_data in raw_data.get("pages", []):
            page_number = page_data.get("page_number", 1)
            page_content = page_data.get("page", {})
            raw_blocks = page_content.get("text_blocks", [])

            if raw_blocks:
                all_x, all_y = [], []
                for block in raw_blocks:
                    bbox = block.get("bbox", {})
                    all_x.extend([bbox.get(f'x{i}', 0) for i in range(1, 5)])
                    all_y.extend([bbox.get(f'y{i}', 0) for i in range(1, 5)])
                width = int(max(all_x)) if all_x else 1500
                height = int(max(all_y)) if all_y else 2000
            else:
                width, height = 1500, 2000

            converted_blocks = []
            for block in raw_blocks:
                converted_blocks.append({
                    "text": block.get("text", ""),
                    "bbox": FormatAdapter.convert_bbox(block.get("bbox", {})),
                    "confidence": block.get("confidence", 0.0),
                })

            normalized_pages.append({
                "document_id": document_id,
                "page_index": page_number,
                "source": "paddleocr",
                "width": width,
                "height": height,
                "doc_name": f"page_{page_number:03d}.png",
                "text_blocks": converted_blocks,
                "metadata": {"page_id": page_data.get("page_id", "")},
            })

        return {
            "document_id": document_id,
            "normalized_pages_data": normalized_pages,
        }

    @staticmethod
    def _convert_flat_format(raw_data: Dict[str, Any]) -> Dict[str, Any]:
        """Convertit le format flat single-page (doc_name + boxes[] au top-level).

        Structure entrée :
          { doc_name, width_page, height_page, boxes: [
              { text, x1, y1, x2, y2, confidence }
          ]}
        """
        document_id = raw_data.get("correlation_id", raw_data.get("uuid", "unknown"))
        doc_name = raw_data.get("doc_name", "unknown.pdf")
        boxes = raw_data.get("boxes", [])
        width = int(raw_data.get("width_page") or 1700)
        height = int(raw_data.get("height_page") or 2200)

        text_blocks = []
        for box in boxes:
            text_blocks.append({
                "text": box.get("text", ""),
                "bbox": [
                    int(box.get("x1", 0)),
                    int(box.get("y1", 0)),
                    int(box.get("x2", 0)),
                    int(box.get("y2", 0)),
                ],
                "confidence": box.get("confidence", 0.0),
            })

        normalized_page = {
            "document_id": document_id,
            "page_index": 1,
            "source": "paddleocr",
            "width": width,
            "height": height,
            "doc_name": doc_name,
            "text_blocks": text_blocks,
            "metadata": {},
        }

        return {
            "document_id": document_id,
            "normalized_pages_data": [normalized_page],
        }

    @staticmethod
    def _convert_broker_format(raw_data: Dict[str, Any]) -> Dict[str, Any]:
        """Convertit le format broker (correlation_id + pages[].ocr_data.boxes[]).

        Structure entrée :
          { correlation_id, doc_name, pages: [
              { page_number, ocr_data: {
                  width_page, height_page, boxes: [
                      { text, x1, y1, x2, y2, confidence }
                  ]
              }}
          ]}
        """
        document_id = raw_data.get("correlation_id", "unknown")
        doc_name = raw_data.get("doc_name", "unknown.pdf")
        normalized_pages = []

        for page_data in raw_data.get("pages", []):
            page_number = page_data.get("page_number", 1)
            ocr = page_data.get("ocr_data", {})
            boxes = ocr.get("boxes", [])
            width = int(ocr.get("width_page", 1700))
            height = int(ocr.get("height_page", 2200))

            text_blocks = []
            for box in boxes:
                text_blocks.append({
                    "text": box.get("text", ""),
                    "bbox": [
                        int(box.get("x1", 0)),
                        int(box.get("y1", 0)),
                        int(box.get("x2", 0)),
                        int(box.get("y2", 0)),
                    ],
                    "confidence": box.get("confidence", 0.0),
                })

            normalized_pages.append({
                "document_id": document_id,
                "page_index": page_number,
                "source": "paddleocr",
                "width": width,
                "height": height,
                "doc_name": doc_name,
                "text_blocks": text_blocks,
                "metadata": {},
            })

        return {
            "document_id": document_id,
            "normalized_pages_data": normalized_pages,
        }
