import pytest
import copy
from app.services.validators import (
    validate_analysis, validate_brand, validate_site_copy,
    validate_package, _check_type,
)


class TestCheckType:
    def test_string(self):
        assert _check_type("hello", "string") is True
        assert _check_type(123, "string") is False

    def test_number(self):
        assert _check_type(42, "number") is True
        assert _check_type(3.14, "number") is True
        assert _check_type("42", "number") is False

    def test_bool(self):
        assert _check_type(True, "bool") is True
        assert _check_type(1, "bool") is False

    def test_list_of_strings(self):
        assert _check_type(["a", "b"], "list_of_strings") is True
        assert _check_type([1, 2], "list_of_strings") is False
        assert _check_type("not a list", "list_of_strings") is False

    def test_list_of_dicts(self):
        assert _check_type([{"a": 1}], "list_of_dicts") is True
        assert _check_type(["not dict"], "list_of_dicts") is False

    def test_dict(self):
        assert _check_type({"a": 1}, "dict") is True
        assert _check_type([1], "dict") is False

    def test_unknown_type_passes(self):
        assert _check_type("anything", "unknown_type") is True


class TestValidateAnalysis:
    def test_valid_analysis_passes(self, sample_analysis):
        result, report = validate_analysis(sample_analysis)
        assert len(report.errors) == 0

    def test_missing_domain_errors(self, sample_analysis):
        del sample_analysis["domain"]
        result, report = validate_analysis(sample_analysis, auto_repair=False)
        assert len(report.errors) > 0

    def test_missing_keywords_errors(self, sample_analysis):
        del sample_analysis["keywords"]
        result, report = validate_analysis(sample_analysis, auto_repair=False)
        assert len(report.errors) > 0

    def test_missing_niches_errors(self, sample_analysis):
        del sample_analysis["niches"]
        result, report = validate_analysis(sample_analysis, auto_repair=False)
        assert len(report.errors) > 0

    def test_empty_niches_errors(self, sample_analysis):
        sample_analysis["niches"] = []
        result, report = validate_analysis(sample_analysis)
        assert len(report.errors) > 0

    def test_auto_repair_coerces_string_keywords(self, sample_analysis):
        sample_analysis["keywords"] = "single-keyword"
        result, report = validate_analysis(sample_analysis, auto_repair=True)
        assert isinstance(result["keywords"], list)
        assert len(report.repairs) > 0

    def test_auto_repair_wraps_single_niche_dict(self, sample_analysis):
        sample_analysis["niches"] = sample_analysis["niches"][0]
        result, report = validate_analysis(sample_analysis, auto_repair=True)
        assert isinstance(result["niches"], list)

    def test_non_dict_input(self):
        result, report = validate_analysis("not a dict")
        assert len(report.errors) > 0

    def test_niche_missing_required_name(self, sample_analysis):
        del sample_analysis["niches"][0]["name"]
        result, report = validate_analysis(sample_analysis, auto_repair=True)
        assert len(report.warnings) > 0

    def test_niche_score_coerced_from_string(self, sample_analysis):
        sample_analysis["niches"][0]["score"] = "8"
        result, report = validate_analysis(sample_analysis, auto_repair=True)
        assert isinstance(result["niches"][0]["score"], float)


class TestValidateBrand:
    def test_valid_brand_passes(self, sample_brand):
        result, report = validate_brand(sample_brand)
        assert len(report.errors) == 0

    def test_missing_options_errors(self, sample_brand):
        del sample_brand["options"]
        result, report = validate_brand(sample_brand, auto_repair=False)
        assert len(report.errors) > 0

    def test_non_dict_input(self):
        result, report = validate_brand("not a dict")
        assert len(report.errors) > 0

    def test_empty_options_still_validates(self, sample_brand):
        sample_brand["options"] = []
        result, report = validate_brand(sample_brand)
        assert isinstance(report.errors, list)
        assert isinstance(report.warnings, list)


class TestValidateSiteCopy:
    def test_empty_dict_warns(self):
        result, report = validate_site_copy({})
        assert len(report.warnings) >= 0

    def test_non_dict_input(self):
        result, report = validate_site_copy("not a dict")
        assert len(report.errors) > 0

    def test_valid_section_passes(self):
        data = {"hero": {"headline": "Hello", "subheadline": "World"}}
        result, report = validate_site_copy(data)
        assert len(report.errors) == 0


class TestValidatePackage:
    def test_minimal_valid_package(self, sample_analysis, sample_brand):
        pkg = {
            "domain": "test.com",
            "analysis": sample_analysis,
            "brand": sample_brand,
            "site_copy": {"hero": {"headline": "Test"}},
        }
        result, report = validate_package(pkg, auto_repair=True)
        assert len(report.errors) == 0

    def test_missing_domain_detected(self, sample_analysis, sample_brand):
        pkg = {
            "analysis": sample_analysis,
            "brand": sample_brand,
            "site_copy": {},
        }
        result, report = validate_package(pkg, auto_repair=False)
        assert len(report.errors) > 0 or len(report.warnings) > 0 or "domain" not in result
