"""Integration tests for full Gherbal workflows."""

import os
import pytest
import pandas as pd
from gherbal import Gherbal, GherbalConfig


class TestFullWorkflow:
    """End-to-end workflow tests."""

    def test_train_evaluate_predict(self, train_df, eval_df, tmp_dir):
        """Train -> Evaluate -> Predict workflow."""
        model = Gherbal.train(
            train_df, tmp_dir,
            train_opts={"epoch": 3, "dim": 30},
        )

        # Evaluate
        acc, df = model.report(eval_df, silent=True)
        assert isinstance(acc, float)
        assert 0.0 <= acc <= 1.0

        # Predict
        result = model.predict("hello world")
        assert isinstance(result, list)
        assert len(result) > 0

    def test_train_save_load_predict(self, train_df, tmp_dir):
        """Train -> Save -> Load -> Predict workflow."""
        save1 = os.path.join(tmp_dir, "train_output")
        save2 = os.path.join(tmp_dir, "saved_model")

        model = Gherbal.train(
            train_df, save1,
            train_opts={"epoch": 3, "dim": 30},
        )
        model.save_pretrained(save2)

        loaded = Gherbal.from_pretrained(save2)
        original_pred = model.predict("the cat sat on the mat", k=1)
        loaded_pred = loaded.predict("the cat sat on the mat", k=1)
        assert original_pred[0][0] == loaded_pred[0][0]

    def test_train_quantize_save_load(self, train_df, tmp_dir):
        """Train -> Quantize -> Save -> Load workflow."""
        train_dir = os.path.join(tmp_dir, "train")
        quant_dir = os.path.join(tmp_dir, "quantized")
        save_dir = os.path.join(tmp_dir, "saved")

        model = Gherbal.train(
            train_df, train_dir,
            train_opts={"epoch": 3, "dim": 30},
        )
        model.quantize(quant_dir)
        model.save_pretrained(save_dir)

        loaded = Gherbal.from_pretrained(save_dir)
        assert loaded.model is not None
        result = loaded.predict("hello world")
        assert isinstance(result, list)

    def test_predict_df_workflow(self, trained_model):
        """predict_df should add correct columns."""
        model, _ = trained_model
        df = pd.DataFrame({
            "text": [
                "the cat sat on the mat",
                "bonjour comment allez vous",
                "مرحبا بالعالم كيف حالكم",
            ],
            "expected": ["eng_Latn", "fra_Latn", "arb_Arab"],
        })

        result = model.predict_df(df)
        assert "lang_predicted" in result.columns

        # Check predictions are reasonable
        for _, row in result.iterrows():
            assert row["lang_predicted"] in model.labels


class TestBackwardsCompatibility:
    """Tests ensuring old import patterns still work."""

    def test_import_gherbal(self):
        from gherbal import Gherbal
        assert Gherbal is not None

    def test_import_config(self):
        from gherbal import GherbalConfig
        assert GherbalConfig is not None

    def test_import_create_clean_script_function(self):
        from gherbal import create_clean_script_function
        fn = create_clean_script_function()
        assert callable(fn)

    def test_import_preprocess_text(self):
        from gherbal import preprocess_text
        result = preprocess_text("Hello World")
        assert isinstance(result, str)

    def test_import_evaluate(self):
        from gherbal import evaluate
        assert callable(evaluate)

    def test_import_report(self):
        from gherbal import report
        assert callable(report)

    def test_import_train_fasttext(self):
        from gherbal import train_fasttext
        assert callable(train_fasttext)

    def test_gherbal_has_preprocess_text_method(self):
        assert hasattr(Gherbal, "preprocess_text")
        assert callable(Gherbal.preprocess_text)

    def test_gherbal_has_clean_text_method(self):
        assert hasattr(Gherbal, "clean_text")
        assert callable(Gherbal.clean_text)

    def test_gherbal_has_remove_emoji_method(self):
        assert hasattr(Gherbal, "remove_emoji")
        assert callable(Gherbal.remove_emoji)

    def test_gherbal_has_create_script_cleaner(self):
        assert hasattr(Gherbal, "create_script_cleaner")
        assert callable(Gherbal.create_script_cleaner)

    def test_gherbal_has_train_classmethod(self):
        assert hasattr(Gherbal, "train")

    def test_gherbal_has_from_dataset_classmethod(self):
        assert hasattr(Gherbal, "from_dataset")


class TestEdgeCases:
    """Edge case and robustness tests."""

    def test_predict_very_long_text(self, trained_model):
        model, _ = trained_model
        long_text = "hello world " * 10000
        result = model.predict(long_text)
        assert isinstance(result, list)

    def test_predict_single_char(self, trained_model):
        model, _ = trained_model
        result = model.predict("a")
        assert isinstance(result, list)

    def test_predict_only_whitespace(self, trained_model):
        model, _ = trained_model
        result = model.predict("   ")
        assert isinstance(result, list)

    def test_predict_mixed_scripts(self, trained_model):
        model, _ = trained_model
        result = model.predict("Hello مرحبا Bonjour")
        assert isinstance(result, list)

    def test_predict_unicode_edge_cases(self, trained_model):
        model, _ = trained_model
        texts = [
            "\u200b",  # Zero-width space
            "\u200e",  # Left-to-right mark
            "café",    # NFD decomposable
            "naïve",   # Diacritics
        ]
        for text in texts:
            result = model.predict(text)
            assert isinstance(result, list)

    def test_config_preserves_model_type(self):
        config = GherbalConfig()
        assert config.model_type == "gherbal"
        d = config.to_dict()
        assert d["model_type"] == "gherbal"

    def test_predict_batch_consistency(self, trained_model):
        """Single predict and batch predict should give same result."""
        model, _ = trained_model
        texts = ["hello world", "bonjour le monde"]

        single_results = [model.predict(t, k=1) for t in texts]
        batch_results = model.predict(texts, k=1)

        for single, batch in zip(single_results, batch_results):
            assert single[0][0] == batch[0][0]
