"""
Tests for enhanced logging and original_file_path fallback in retry-subtask endpoint.
"""

import os
import shutil
from unittest.mock import MagicMock, patch

import pytest
from fastapi.testclient import TestClient

from app.models import FileRecord


@pytest.mark.integration
class TestRetrySubtaskEnhancedLogging:
    """Tests for enhanced logging in retry-subtask endpoint."""

    def test_embed_metadata_retry_with_original_file_path(
        self, client: TestClient, db_session, sample_pdf_path, tmp_path
    ):
        """Test that embed_metadata_into_pdf retry uses original_file_path as fallback."""
        mock_task = MagicMock()
        mock_task.id = "embed-original-fallback-task"

        # Create original directory and copy file there
        original_dir = tmp_path / "original"
        original_dir.mkdir(exist_ok=True)
        original_file = original_dir / "original_doc.pdf"
        shutil.copy(sample_pdf_path, original_file)

        # Create file record with non-existent local_filename and processed_file_path
        # but existing original_file_path
        file_record = FileRecord(
            filehash="embed_original_test",
            original_filename="original_doc.pdf",
            local_filename="/nonexistent/tmp/doc.pdf",  # File no longer in tmp
            processed_file_path="/nonexistent/processed/doc.pdf",  # Also not in processed
            original_file_path=str(original_file),  # But exists in original
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        with patch("app.tasks.extract_metadata_with_gpt.extract_metadata_with_gpt") as mock_extract:
            mock_extract.delay.return_value = mock_task
            response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=embed_metadata_into_pdf")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "success"
        assert data["subtask_name"] == "embed_metadata_into_pdf"

        # Verify the task was called with the original_file_path
        mock_extract.delay.assert_called_once()
        call_args = mock_extract.delay.call_args
        # First argument should be the file path (original_file_path in this case)
        assert call_args[0][0] == str(original_file)

    def test_embed_metadata_retry_error_includes_all_checked_paths(self, client: TestClient, db_session):
        """Test that error message includes all checked paths with their existence status."""
        # Create file record with all non-existent paths
        file_record = FileRecord(
            filehash="embed_error_paths_test",
            original_filename="missing.pdf",
            local_filename="/nonexistent/tmp/missing.pdf",
            processed_file_path="/nonexistent/processed/missing.pdf",
            original_file_path="/nonexistent/original/missing.pdf",
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=embed_metadata_into_pdf")

        assert response.status_code == 400
        error_detail = response.json()["detail"]

        # Verify the error message contains diagnostic information
        assert "Cannot retry metadata embedding" in error_detail
        assert "Paths checked:" in error_detail
        assert "local_filename" in error_detail
        assert "processed_file_path" in error_detail
        assert "original_file_path" in error_detail
        assert "workdir_tmp_fallback" in error_detail
        assert "exists=False" in error_detail

    def test_process_document_error_includes_checked_path(self, client: TestClient, db_session):
        """Test that process_document error message includes checked path."""
        file_record = FileRecord(
            filehash="process_error_test",
            original_filename="missing.pdf",
            local_filename="/nonexistent/tmp/missing.pdf",
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=process_document")

        assert response.status_code == 400
        error_detail = response.json()["detail"]

        # Verify the error message contains diagnostic information
        assert "Cannot retry" in error_detail
        assert "Path checked:" in error_detail
        assert "local_filename" in error_detail
        assert "exists=False" in error_detail

    def test_ocr_retry_error_includes_checked_path(self, client: TestClient, db_session):
        """Test that OCR retry error message includes checked path."""
        file_record = FileRecord(
            filehash="ocr_error_test",
            original_filename="missing.pdf",
            local_filename="/nonexistent/tmp/missing.pdf",
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        response = client.post(
            f"/api/files/{file_record.id}/retry-subtask?subtask_name=process_with_azure_document_intelligence"
        )

        assert response.status_code == 400
        error_detail = response.json()["detail"]

        # Verify the error message contains diagnostic information
        assert "Cannot retry OCR" in error_detail
        assert "Path checked:" in error_detail
        assert "local_filename" in error_detail
        assert "exists=False" in error_detail

    def test_metadata_extraction_error_includes_checked_path(self, client: TestClient, db_session):
        """Test that metadata extraction retry error message includes checked path."""
        file_record = FileRecord(
            filehash="metadata_error_test",
            original_filename="missing.pdf",
            local_filename="/nonexistent/tmp/missing.pdf",
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=extract_metadata_with_gpt")

        assert response.status_code == 400
        error_detail = response.json()["detail"]

        # Verify the error message contains diagnostic information
        assert "Cannot retry metadata extraction" in error_detail
        assert "Path checked:" in error_detail
        assert "local_filename" in error_detail
        assert "exists=False" in error_detail

    def test_upload_retry_error_includes_all_checked_paths(self, client: TestClient, db_session):
        """Test that upload retry error message includes all checked paths."""
        file_record = FileRecord(
            filehash="upload_error_test",
            original_filename="missing.pdf",
            local_filename="/nonexistent/tmp/missing.pdf",
            processed_file_path="/nonexistent/processed/missing_gpt.pdf",
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=upload_to_dropbox")

        assert response.status_code == 400
        error_detail = response.json()["detail"]

        # Verify the error message contains diagnostic information
        assert "Cannot retry upload" in error_detail
        assert "Paths checked:" in error_detail
        assert "processed_file_path" in error_detail
        assert "exists=False" in error_detail

    def test_upload_retry_uses_processed_file_path_from_db(
        self, client: TestClient, db_session, sample_pdf_path, tmp_path
    ):
        """Test that upload retry uses processed_file_path from DB when legacy paths don't exist."""
        mock_task = MagicMock()
        mock_task.id = "upload-processed-path-task"

        # Create processed file at the DB-stored path (GPT-suggested filename)
        processed_file = tmp_path / "2023-10-01_Unknown.pdf"
        shutil.copy(sample_pdf_path, processed_file)

        # Create file record where only processed_file_path exists
        # (simulates the real scenario: original filename != GPT-suggested filename)
        file_record = FileRecord(
            filehash="upload_db_path_test",
            original_filename="cable_graphic.pdf",
            local_filename="/nonexistent/tmp/uuid.pdf",
            processed_file_path=str(processed_file),
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        # Verify the legacy paths do NOT exist (ensures test validates DB-path behavior)
        from app.config import settings

        workdir = settings.workdir
        legacy_hash_path = os.path.join(workdir, "processed", f"{file_record.filehash}.pdf")
        legacy_original_path = os.path.join(workdir, "processed", file_record.original_filename)
        assert not os.path.exists(legacy_hash_path)
        assert not os.path.exists(legacy_original_path)

        with patch("app.tasks.upload_to_onedrive.upload_to_onedrive") as mock_upload:
            mock_upload.delay.return_value = mock_task
            response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=upload_to_onedrive")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "success"
        assert data["subtask_name"] == "upload_to_onedrive"

        # Verify the task was called with the processed_file_path from DB
        mock_upload.delay.assert_called_once()
        call_args = mock_upload.delay.call_args
        assert call_args[0][0] == str(processed_file)

    def test_upload_retry_processed_file_path_takes_priority(
        self, client: TestClient, db_session, sample_pdf_path, tmp_path
    ):
        """Test that processed_file_path from DB takes priority over legacy hash-based paths."""
        mock_task = MagicMock()
        mock_task.id = "upload-priority-task"

        # Create the DB-stored processed file
        processed_file = tmp_path / "2024-01-01_Invoice.pdf"
        shutil.copy(sample_pdf_path, processed_file)

        # Also create a competing legacy hash-based file in the workdir/processed/ directory
        from app.config import settings

        workdir = settings.workdir
        processed_dir = os.path.join(workdir, "processed")
        os.makedirs(processed_dir, exist_ok=True)
        legacy_hash_file = os.path.join(processed_dir, "upload_priority_test.pdf")
        shutil.copy(sample_pdf_path, legacy_hash_file)

        file_record = FileRecord(
            filehash="upload_priority_test",
            original_filename="scan001.pdf",
            local_filename="/nonexistent/tmp/uuid.pdf",
            processed_file_path=str(processed_file),
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        with patch("app.tasks.upload_to_dropbox.upload_to_dropbox") as mock_upload:
            mock_upload.delay.return_value = mock_task
            response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=upload_to_dropbox")

        assert response.status_code == 200

        # Verify the task was called with the processed_file_path (first priority for uploads)
        # even though a legacy hash-based file also exists
        mock_upload.delay.assert_called_once()
        call_args = mock_upload.delay.call_args
        assert call_args[0][0] == str(processed_file)

    def test_embed_metadata_path_order(self, client: TestClient, db_session, sample_pdf_path, tmp_path):
        """Test that embed_metadata_into_pdf checks paths in the correct order."""
        mock_task = MagicMock()
        mock_task.id = "embed-path-order-task"

        # Create all three directories with files
        local_file = tmp_path / "local_file.pdf"
        processed_dir = tmp_path / "processed"
        processed_dir.mkdir(exist_ok=True)
        processed_file = processed_dir / "processed_file.pdf"
        original_dir = tmp_path / "original"
        original_dir.mkdir(exist_ok=True)
        original_file = original_dir / "original_file.pdf"

        # Copy sample PDF to all locations
        shutil.copy(sample_pdf_path, local_file)
        shutil.copy(sample_pdf_path, processed_file)
        shutil.copy(sample_pdf_path, original_file)

        # Create file record with all paths existing
        file_record = FileRecord(
            filehash="embed_path_order_test",
            original_filename="doc.pdf",
            local_filename=str(local_file),
            processed_file_path=str(processed_file),
            original_file_path=str(original_file),
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        with patch("app.tasks.extract_metadata_with_gpt.extract_metadata_with_gpt") as mock_extract:
            mock_extract.delay.return_value = mock_task
            response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=embed_metadata_into_pdf")

        assert response.status_code == 200

        # Verify the task was called with the local_filename (first priority)
        mock_extract.delay.assert_called_once()
        call_args = mock_extract.delay.call_args
        assert call_args[0][0] == str(local_file)

    def test_embed_metadata_prefers_processed_over_original(
        self, client: TestClient, db_session, sample_pdf_path, tmp_path
    ):
        """Test that when local_filename is missing, processed_file_path is preferred over original_file_path."""
        mock_task = MagicMock()
        mock_task.id = "embed-processed-priority-task"

        # Create processed and original directories with files
        processed_dir = tmp_path / "processed"
        processed_dir.mkdir(exist_ok=True)
        processed_file = processed_dir / "processed_file.pdf"
        original_dir = tmp_path / "original"
        original_dir.mkdir(exist_ok=True)
        original_file = original_dir / "original_file.pdf"

        # Copy sample PDF to both locations
        shutil.copy(sample_pdf_path, processed_file)
        shutil.copy(sample_pdf_path, original_file)

        # Create file record with non-existent local_filename but existing processed and original
        file_record = FileRecord(
            filehash="embed_processed_priority_test",
            original_filename="doc.pdf",
            local_filename="/nonexistent/tmp/doc.pdf",
            processed_file_path=str(processed_file),
            original_file_path=str(original_file),
            file_size=1024,
            mime_type="application/pdf",
        )
        db_session.add(file_record)
        db_session.commit()
        db_session.refresh(file_record)

        with patch("app.tasks.extract_metadata_with_gpt.extract_metadata_with_gpt") as mock_extract:
            mock_extract.delay.return_value = mock_task
            response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=embed_metadata_into_pdf")

        assert response.status_code == 200

        # Verify the task was called with the processed_file_path (second priority)
        mock_extract.delay.assert_called_once()
        call_args = mock_extract.delay.call_args
        assert call_args[0][0] == str(processed_file)
