"""Tests for core BizFrame and Pipeline."""

import numpy as np
import pandas as pd
import pytest

from bizanalyticsai.core.dataframe import BizFrame
from bizanalyticsai.core.pipeline  import Pipeline


@pytest.fixture
def sample_df():
    np.random.seed(42)
    return pd.DataFrame({
        "customer_id": range(100),
        "revenue":     np.random.lognormal(10, 1, 100),
        "cost":        np.random.lognormal(9, 1, 100),
        "segment":     np.random.choice(["A", "B", "C"], 100),
        "date":        pd.date_range("2023-01-01", periods=100, freq="D"),
        "country":     np.random.choice(["BR", "AR", "MX", None], 100),
    })


class TestBizFrame:
    def test_is_dataframe(self, sample_df):
        bf = BizFrame(sample_df)
        assert isinstance(bf, pd.DataFrame)
        assert isinstance(bf, BizFrame)

    def test_profile_returns_dict(self, sample_df):
        bf = BizFrame(sample_df)
        p = bf.profile()
        assert "shape" in p
        assert p["shape"]["rows"] == 100
        assert "columns" in p
        assert "revenue" in p["columns"]

    def test_quality_returns_bizframe(self, sample_df):
        bf = BizFrame(sample_df)
        q = bf.quality()
        assert isinstance(q, BizFrame)
        assert "column" in q.columns
        assert "flags" in q.columns
        assert len(q) == len(sample_df.columns)

    def test_segment_adds_column(self, sample_df):
        bf = BizFrame(sample_df)
        out = bf.segment("revenue", bins=3)
        assert "revenue_segment" in out.columns

    def test_top_returns_n_rows(self, sample_df):
        bf = BizFrame(sample_df)
        top = bf.top(5, "revenue")
        assert len(top) == 5
        assert top["revenue"].is_monotonic_decreasing

    def test_growth_adds_column(self, sample_df):
        bf = BizFrame(sample_df)
        out = bf.growth("revenue")
        assert "revenue_growth_pct" in out.columns

    def test_anomalies_adds_flag(self, sample_df):
        bf = BizFrame(sample_df)
        out = bf.anomalies("revenue")
        assert "revenue_anomaly" in out.columns
        assert "revenue_anomaly_type" in out.columns

    def test_chunks_yields_bizframes(self, sample_df):
        bf = BizFrame(sample_df)
        chunks = list(bf.chunks(size=30))
        assert len(chunks) == 4  # 100 / 30 = ceil 4
        for c in chunks:
            assert isinstance(c, BizFrame)

    def test_to_biz_dict_structure(self, sample_df):
        bf = BizFrame(sample_df)
        d = bf.to_biz_dict()
        assert "meta" in d
        assert "data" in d
        assert d["meta"]["rows"] == 100
        assert "checksum" in d["meta"]

    def test_constructor_preserves_type(self, sample_df):
        bf = BizFrame(sample_df)
        # Slicing should return BizFrame
        subset = bf[bf["revenue"] > 1000]
        assert isinstance(subset, BizFrame)


class TestPipeline:
    def test_basic_pipeline(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.step("passthrough", lambda df: BizFrame(df))
        result = pipe.run(BizFrame(sample_df))
        assert isinstance(result, BizFrame)
        assert len(result) == 100

    def test_filter_step(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.filter("revenue > 10000")
        result = pipe.run(BizFrame(sample_df))
        assert len(result) < 100
        assert (result["revenue"] > 10000).all()

    def test_select_step(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.select("customer_id", "revenue")
        result = pipe.run(BizFrame(sample_df))
        assert list(result.columns) == ["customer_id", "revenue"]

    def test_limit_step(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.limit(10)
        result = pipe.run(BizFrame(sample_df))
        assert len(result) == 10

    def test_rename_step(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.rename({"revenue": "rev"})
        result = pipe.run(BizFrame(sample_df))
        assert "rev" in result.columns
        assert "revenue" not in result.columns

    def test_sort_step(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.sort("revenue", ascending=False)
        result = pipe.run(BizFrame(sample_df))
        assert result["revenue"].is_monotonic_decreasing

    def test_chained_steps(self, sample_df):
        result = (
            Pipeline("chain", verbose=False)
            .filter("revenue > 5000")
            .select("customer_id", "revenue", "segment")
            .limit(20)
            .sort("revenue", ascending=False)
            .run(BizFrame(sample_df))
        )
        assert len(result) <= 20
        assert set(result.columns) == {"customer_id", "revenue", "segment"}

    def test_report_returns_string(self, sample_df):
        pipe = Pipeline("test", verbose=False)
        pipe.limit(5)
        pipe.run(BizFrame(sample_df))
        report = pipe.report()
        assert isinstance(report, str)
        assert "test" in report
