"""Tests for filing fetch functionality."""

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

from secbulkdownload.secmeta import idx
from secbulkdownload.utils import fetch_sec_file


class TestFilingPathComputation:
    """Test filing path computation logic."""

    def test_filing_path_structure(self, datadir: str) -> None:
        """Test that filing paths follow the canonical structure."""
        # Patch updateidx to avoid network calls during init
        with patch.object(idx, "updateidx", return_value=[]):
            test_idx = idx(datadir=datadir, user_agent="Test Co test@test.com")

        # Create a mock working_idx as list of dicts
        test_idx.working_idx = [
            {
                "CIK": "0000320193",
                "Company Name": "Apple Inc.",
                "Form Type": "10-K",
                "Date Filed": "2023-01-01",
                "Filename": "edgar/data/0000320193/0000320193-23-000077.txt",
            }
        ]

        # Build filing paths
        filing_paths = []
        for row in test_idx.working_idx:
            sec_filepath = row["Filename"]
            filing_url = test_idx.sec_base + sec_filepath
            _, _, cik, fname = sec_filepath.split("/")
            localfilepath = os.sep.join([test_idx.filingsdir, cik, fname])
            filing_paths.append((filing_url, f"{localfilepath}.xz"))

        # Verify structure
        expected_url = "https://www.sec.gov/Archives/edgar/data/0000320193/0000320193-23-000077.txt"
        expected_local = os.path.join(datadir, "filings", "0000320193", "0000320193-23-000077.txt.xz")

        assert len(filing_paths) == 1
        assert filing_paths[0][0] == expected_url
        assert filing_paths[0][1] == expected_local

    def test_accession_extraction(self, datadir: str) -> None:
        """Test that accession number is correctly extracted."""
        # Test with various filename formats
        sec_filepath = "edgar/data/0000320193/0000320193-23-000077.txt"
        _, _, cik, fname = sec_filepath.split("/")
        
        # Accession is the filename without .txt extension
        accession = fname.replace(".txt", "")
        assert accession == "0000320193-23-000077"
        assert cik == "0000320193"


class TestFetchSecFile:
    """Tests for fetch_sec_file function."""

    def test_fetch_creates_parent_dirs(self, datadir: str) -> None:
        """Test that fetch_sec_file creates parent directories."""
        localpath = os.path.join(datadir, "subdir", "deep", "file.txt.xz")
        
        # Verify parent doesn't exist
        assert not os.path.exists(os.path.dirname(localpath))

        with patch("secbulkdownload.utils.urllib.request.urlopen") as mock_urlopen:
            mock_response = MagicMock()
            mock_response.read.return_value = b"test content"
            mock_response.__enter__ = MagicMock(return_value=mock_response)
            mock_response.__exit__ = MagicMock(return_value=False)
            mock_urlopen.return_value = mock_response

            result = fetch_sec_file(
                "https://www.sec.gov/test.txt",
                localpath,
                user_agent="Test Co test@test.com",
            )

            # Verify parent directory was created
            assert os.path.exists(os.path.dirname(localpath))
            assert result == localpath

    def test_fetch_compresses_non_binary_files(self, datadir: str) -> None:
        """Test that non-binary files are LZMA compressed."""
        localpath = os.path.join(datadir, "test.txt.xz")
        test_content = b"test content for compression"

        with patch("secbulkdownload.utils.urllib.request.urlopen") as mock_urlopen:
            mock_response = MagicMock()
            mock_response.read.return_value = test_content
            mock_response.__enter__ = MagicMock(return_value=mock_response)
            mock_response.__exit__ = MagicMock(return_value=False)
            mock_urlopen.return_value = mock_response

            fetch_sec_file(
                "https://www.sec.gov/test.txt",
                localpath,
                user_agent="Test Co test@test.com",
            )

            # Verify file was created and is LZMA compressed
            assert os.path.exists(localpath)
            with open(localpath, "rb") as f:
                decompressed = lzma.decompress(f.read())
            assert decompressed == test_content

    def test_fetch_binary_files_not_compressed(self, datadir: str) -> None:
        """Test that binary files (gz, zip, Z) are saved as-is."""
        localpath = os.path.join(datadir, "test.gz")
        test_content = b"gz binary content"

        with patch("secbulkdownload.utils.urllib.request.urlopen") as mock_urlopen:
            mock_response = MagicMock()
            mock_response.read.return_value = test_content
            mock_response.__enter__ = MagicMock(return_value=mock_response)
            mock_response.__exit__ = MagicMock(return_value=False)
            mock_urlopen.return_value = mock_response

            fetch_sec_file(
                "https://www.sec.gov/test.gz",
                localpath,
                user_agent="Test Co test@test.com",
            )

            # Verify file was saved as-is (not compressed)
            assert os.path.exists(localpath)
            with open(localpath, "rb") as f:
                content = f.read()
            assert content == test_content

    def test_fetch_sets_user_agent_header(self, datadir: str) -> None:
        """Test that User-Agent header is set correctly."""
        localpath = os.path.join(datadir, "test.txt.xz")

        with patch("secbulkdownload.utils.urllib.request.urlopen") as mock_urlopen:
            mock_response = MagicMock()
            mock_response.read.return_value = b"test"
            mock_response.__enter__ = MagicMock(return_value=mock_response)
            mock_response.__exit__ = MagicMock(return_value=False)
            mock_urlopen.return_value = mock_response

            with patch("secbulkdownload.utils.urllib.request.Request") as mock_request:
                mock_req = MagicMock()
                mock_request.return_value = mock_req

                fetch_sec_file(
                    "https://www.sec.gov/test.txt",
                    localpath,
                    user_agent="Test Company test@test.com",
                )

                # Verify User-Agent was added
                mock_req.add_header.assert_any_call("User-Agent", "Test Company test@test.com")


class TestLZMARoundTrip:
    """Test LZMA compression round-trip."""

    def test_lzma_roundtrip(self, datadir: str) -> None:
        """Test that data can be compressed and decompressed correctly."""
        original_data = b"This is test data that should be compressed and then decompressed correctly."
        localpath = os.path.join(datadir, "roundtrip.txt.xz")

        # Write compressed
        compressed = lzma.compress(original_data)
        with open(localpath, "wb") as f:
            f.write(compressed)

        # Read and decompress
        with open(localpath, "rb") as f:
            decompressed = lzma.decompress(f.read())

        assert decompressed == original_data


class TestIdempotentDownloads:
    """Test idempotent download behavior."""

    def test_fetch_filings_skips_existing(self, datadir: str) -> None:
        """Test that fetch_filings skips files that already exist locally."""
        # Patch updateidx to avoid network calls during init
        with patch.object(idx, "updateidx", return_value=[]):
            test_idx = idx(datadir=datadir, user_agent="Test Co test@test.com")

        # Create a mock working_idx as list of dicts
        test_idx.working_idx = [
            {
                "CIK": "0000320193",
                "Company Name": "Apple Inc.",
                "Form Type": "10-K",
                "Date Filed": "2023-01-01",
                "Filename": "edgar/data/0000320193/0000320193-23-000077.txt",
            }
        ]

        # Create the expected filing file
        filings_dir = os.path.join(datadir, "filings", "0000320193")
        os.makedirs(filings_dir, exist_ok=True)
        filing_path = os.path.join(filings_dir, "0000320193-23-000077.txt.xz")

        # Pre-create the file
        with open(filing_path, "wb") as f:
            f.write(lzma.compress(b"existing content"))

        # Fetch filings - should not make network calls
        with patch("secbulkdownload.secmeta.fetch_sec_file") as mock_fetch:
            test_idx.fetch_filings(verbose=False)
            # fetch_sec_file should not be called since file exists
            mock_fetch.assert_not_called()

    def test_fetch_filings_downloads_missing(self, datadir: str) -> None:
        """Test that fetch_filings downloads missing files."""
        # Patch updateidx to avoid network calls during init
        with patch.object(idx, "updateidx", return_value=[]):
            test_idx = idx(datadir=datadir, user_agent="Test Co test@test.com")

        # Create a mock working_idx as list of dicts
        test_idx.working_idx = [
            {
                "CIK": "0000320193",
                "Company Name": "Apple Inc.",
                "Form Type": "10-K",
                "Date Filed": "2023-01-01",
                "Filename": "edgar/data/0000320193/0000320193-23-000077.txt",
            }
        ]

        # Patch fetch_sec_file in secmeta module (where fetch_filings calls it)
        with patch("secbulkdownload.secmeta.fetch_sec_file") as mock_fetch:
            mock_fetch.return_value = "/some/path"
            test_idx.fetch_filings(verbose=False)
            # fetch_sec_file should be called once for the missing file
            mock_fetch.assert_called_once()
