import pytest
from unittest.mock import patch, AsyncMock
from app.services.marketplace import (
    score_package_completeness,
    calculate_listing_price,
    get_ebay_category,
    get_marketplace_summary,
    get_all_tiers,
    PRICING_TIERS,
    PACKAGE_COMPONENTS,
    EBAY_CATEGORIES,
    _build_upgrade_path,
    _score_label,
    _ebay_title_prefix,
)


class TestPackageCompleteness:
    def test_empty_package_scores_zero(self):
        result = score_package_completeness({})
        assert result["score"] == 0
        assert result["earned_weight"] == 0
        assert len(result["present"]) == 0
        assert len(result["missing"]) == len(PACKAGE_COMPONENTS)

    def test_minimal_package_scores_low(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        result = score_package_completeness(pkg)
        assert result["score"] > 0
        assert result["score"] < 20
        assert any(p["id"] == "domain_analysis" for p in result["present"])

    def test_full_starter_package(self):
        pkg = {
            "domain": "test.com",
            "niche": "fitness",
            "brand_name": "FitPro",
            "tagline": "Get Fit Now",
            "brand_colors": ["#FF0000"],
            "site_copy": {f"section_{i}": "content" for i in range(4)},
        }
        result = score_package_completeness(pkg)
        assert result["score"] > 15
        assert result["recommended_tier"] in ("starter", "professional")

    def test_professional_package(self):
        pkg = {
            "domain": "test.com",
            "niche": "fitness",
            "brand_name": "FitPro",
            "tagline": "Get Fit Now",
            "brand_colors": ["#FF0000"],
            "site_copy": {f"section_{i}": "content" for i in range(12)},
            "hero_image_url": "https://example.com/hero.jpg",
            "sales_letter": "A" * 300,
            "theme": {"primary": "#000"},
        }
        result = score_package_completeness(pkg)
        assert result["score"] > 40
        present_ids = {p["id"] for p in result["present"]}
        assert "full_site_copy" in present_ids
        assert "hero_images" in present_ids
        assert "sales_letter" in present_ids

    def test_premium_package_includes_force_multiplier(self):
        pkg = {
            "domain": "test.com",
            "niche": "fitness",
            "brand_name": "FitPro",
            "tagline": "Get Fit Now",
            "brand_colors": ["#FF0000"],
            "site_copy": {f"section_{i}": "content" for i in range(12)},
            "hero_image_url": "https://example.com/hero.jpg",
            "sales_letter": "A" * 300,
            "theme": {"primary": "#000"},
            "business_docs": [f"doc_{i}" for i in range(15)],
            "graphics_pack": ["graphic1.png"],
            "ftp_config": {"host": "example.com"},
        }
        result = score_package_completeness(pkg)
        assert result["score"] > 70
        assert result["recommended_tier"] in ("premium", "legendary")

    def test_upgrade_path_shows_missing_components(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        result = score_package_completeness(pkg)
        assert len(result["upgrade_path"]) > 0
        for step in result["upgrade_path"]:
            assert "tier" in step
            assert "components_needed" in step
            assert step["components_count"] > 0

    def test_score_never_exceeds_100(self):
        pkg = {
            "domain": "x.com",
            "niche": "all",
            "brand_name": "X",
            "tagline": "Y",
            "brand_colors": ["#000"],
            "site_copy": {f"s{i}": "c" for i in range(20)},
            "hero_images": ["a.jpg"],
            "sales_letter": "A" * 500,
            "theme": {"x": 1},
            "business_docs": [f"d{i}" for i in range(35)],
            "graphics_pack": ["g.png"],
            "deployed": True,
            "market_research": {"x": 1},
            "competitor_analysis": {"x": 1},
            "seo_strategy": {"x": 1},
            "ad_copy": {"x": 1},
            "email_sequences": {"x": 1},
        }
        result = score_package_completeness(pkg)
        assert result["score"] <= 100


class TestPricingTiers:
    def test_all_tiers_have_required_fields(self):
        for tier_id, tier in PRICING_TIERS.items():
            assert "label" in tier
            assert "price_range" in tier
            assert "target_margin" in tier
            assert "includes" in tier
            assert len(tier["includes"]) > 0

    def test_tier_prices_increase(self):
        order = ["starter", "professional", "premium", "legendary"]
        for i in range(len(order) - 1):
            low_tier = PRICING_TIERS[order[i]]["price_range"]
            high_tier = PRICING_TIERS[order[i + 1]]["price_range"]
            assert high_tier[0] > low_tier[0], f"{order[i+1]} should cost more than {order[i]}"

    def test_each_tier_includes_subset_of_components(self):
        valid_components = set(PACKAGE_COMPONENTS.keys())
        for tier_id, tier in PRICING_TIERS.items():
            for comp in tier["includes"]:
                assert comp in valid_components, f"Tier '{tier_id}' references unknown component '{comp}'"


class TestCalculateListingPrice:
    def test_basic_pricing(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        result = calculate_listing_price(pkg, "starter")
        assert result["tier"] == "starter"
        assert result["suggested_price"] > 0
        assert result["estimated_profit"] > 0

    def test_high_value_niche_multiplier(self):
        base_pkg = {"domain": "test.com", "niche": "gardening"}
        premium_pkg = {"domain": "test.com", "niche": "ai software saas"}
        base_price = calculate_listing_price(base_pkg, "professional")
        premium_price = calculate_listing_price(premium_pkg, "professional")
        assert premium_price["niche_multiplier"] > base_price["niche_multiplier"]
        assert premium_price["suggested_price"] > base_price["suggested_price"]

    def test_auto_tier_detection(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        result = calculate_listing_price(pkg)
        assert result["tier"] in PRICING_TIERS

    def test_completeness_affects_price(self):
        sparse = {"domain": "test.com", "niche": "fitness"}
        rich = {
            "domain": "test.com",
            "niche": "fitness",
            "brand_name": "FitPro",
            "tagline": "Y",
            "brand_colors": ["#000"],
            "site_copy": {f"s{i}": "c" for i in range(12)},
            "hero_images": ["x"],
            "sales_letter": "A" * 300,
            "theme": {"x": 1},
        }
        sparse_price = calculate_listing_price(sparse, "professional")
        rich_price = calculate_listing_price(rich, "professional")
        assert rich_price["completeness_multiplier"] >= sparse_price["completeness_multiplier"]


class TestEbayCategory:
    def test_website_niche_maps_to_websites(self):
        result = get_ebay_category("website design agency")
        assert result["primary_category_id"] == EBAY_CATEGORIES["websites"]["id"]

    def test_domain_niche_maps_to_domains(self):
        result = get_ebay_category("premium domain names")
        assert result["primary_category_id"] == EBAY_CATEGORIES["domains"]["id"]

    def test_ebook_niche_maps_to_digital(self):
        result = get_ebay_category("ebook publishing")
        assert result["primary_category_id"] == EBAY_CATEGORIES["digital_products"]["id"]

    def test_generic_niche_maps_to_startup(self):
        result = get_ebay_category("pet grooming")
        assert result["primary_category_id"] == EBAY_CATEGORIES["startup_business"]["id"]

    def test_listing_type_varies_by_tier(self):
        basic = get_ebay_category("fitness", "starter")
        premium = get_ebay_category("fitness", "premium")
        assert "Auction" in basic["listing_type_recommendation"]
        assert "Buy It Now" == premium["listing_type_recommendation"]

    def test_title_prefix_includes_tier(self):
        result = get_ebay_category("fitness", "legendary")
        assert "LEGENDARY" in result["suggested_ebay_title_prefix"]


class TestMarketplaceSummary:
    def test_summary_includes_all_sections(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        result = get_marketplace_summary(pkg)
        assert "completeness" in result
        assert "pricing" in result
        assert "category" in result
        assert "tiers" in result
        assert "package_value_summary" in result

    def test_ready_to_list_threshold(self):
        sparse = {"domain": "test.com", "niche": "fitness"}
        result = get_marketplace_summary(sparse)
        assert result["package_value_summary"]["ready_to_list"] is False

    def test_all_tiers_returned(self):
        tiers = get_all_tiers()
        assert len(tiers) == len(PRICING_TIERS)
        for tier_id, tier in tiers.items():
            assert "component_details" in tier


class TestScoreLabel:
    def test_legendary(self):
        assert _score_label(95) == "Legendary"

    def test_premium(self):
        assert _score_label(80) == "Premium"

    def test_professional(self):
        assert _score_label(60) == "Professional"

    def test_starter(self):
        assert _score_label(30) == "Starter"

    def test_incomplete(self):
        assert _score_label(10) == "Incomplete"


class TestEbayTitlePrefix:
    def test_starter_prefix(self):
        result = _ebay_title_prefix("Fitness", "starter")
        assert "Starter" in result
        assert "Fitness" in result

    def test_legendary_prefix(self):
        result = _ebay_title_prefix("AI SaaS", "legendary")
        assert "LEGENDARY" in result

    def test_long_niche_truncated(self):
        long_niche = "A" * 100
        result = _ebay_title_prefix(long_niche, "professional")
        assert len(result) < 120


class TestFrontendContractCompliance:
    """Contract tests ensuring API response keys match what the JS frontend expects.

    The marketplace.html JS template references specific field names from the
    summary, pricing, and completeness responses. If the backend changes field
    names without updating the JS, users see 'Cannot read properties of undefined'
    errors. These tests prevent that class of integration bugs.
    """

    def test_summary_pricing_has_js_required_fields(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        summary = get_marketplace_summary(pkg)
        pricing = summary["pricing"]
        assert "tier_label" in pricing or "tier" in pricing, "JS needs tier_label or tier"
        assert "adjusted_range" in pricing, "JS references pricing.adjusted_range[0]/[1]"
        assert "suggested_price" in pricing, "JS references pricing.suggested_price"
        rng = pricing["adjusted_range"]
        assert hasattr(rng, '__getitem__') and len(rng) == 2, "adjusted_range must be indexable with 2 elements"

    def test_summary_completeness_has_js_required_fields(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        summary = get_marketplace_summary(pkg)
        comp = summary["completeness"]
        assert "score" in comp, "JS references comp.score"
        assert "present" in comp, "JS references comp.present"
        assert "missing" in comp, "JS references comp.missing"
        assert isinstance(comp["present"], list)
        assert isinstance(comp["missing"], list)

    def test_summary_category_has_js_required_fields(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        summary = get_marketplace_summary(pkg)
        cat = summary.get("category", {})
        assert "primary_category_name" in cat, "JS references cat.primary_category_name"
        assert "primary_category_id" in cat, "JS references cat.primary_category_id"

    def test_summary_package_value_summary_has_js_required_fields(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        summary = get_marketplace_summary(pkg)
        pvs = summary["package_value_summary"]
        assert "score_label" in pvs, "JS references pvs.score_label"
        assert "ready_to_list" in pvs

    def test_calculate_listing_price_field_contract(self):
        pkg = {"domain": "test.com", "niche": "fitness"}
        result = calculate_listing_price(pkg, "professional")
        required_fields = ["tier", "tier_label", "base_range", "adjusted_range",
                           "suggested_price", "niche_multiplier", "completeness_multiplier",
                           "completeness_score", "estimated_cost", "estimated_profit", "target_margin"]
        for field in required_fields:
            assert field in result, f"Missing required field '{field}' in pricing response"
