#!/usr/bin/env python3
"""
Chinese-Style Shared Dinner Fairness Analysis
Evaluates menu fairness across 10 diners with diverse constraints and preferences

Key Rule: In a shared dinner, diners only need a safe meal path (staple + main + vegetable).
The presence of unsafe dishes on the table is not a failure for other diners.
"""

import pandas as pd
import json
from datetime import datetime

# ============================================================================
# DINER PROFILES
# ============================================================================

diners = [
    {
        "id": 1,
        "name": "Alice",
        "allergies": ["peanuts", "tree nuts"],
        "dietary_restrictions": [],
        "budget_per_person": 50,
        "cuisine_preferences": ["Sichuan", "Cantonese"],
        "dislikes": ["organ meats", "sea cucumber"]
    },
    {
        "id": 2,
        "name": "Bob",
        "allergies": [],
        "dietary_restrictions": ["vegetarian"],
        "budget_per_person": 40,
        "cuisine_preferences": ["Cantonese", "Shanghai"],
        "dislikes": ["spicy food"]
    },
    {
        "id": 3,
        "name": "Carol",
        "allergies": ["shellfish"],
        "dietary_restrictions": [],
        "budget_per_person": 60,
        "cuisine_preferences": ["Sichuan", "Hunan"],
        "dislikes": ["bitter melon"]
    },
    {
        "id": 4,
        "name": "David",
        "allergies": [],
        "dietary_restrictions": ["halal"],
        "budget_per_person": 45,
        "cuisine_preferences": ["Xinjiang", "Beijing"],
        "dislikes": ["tofu"]
    },
    {
        "id": 5,
        "name": "Emma",
        "allergies": ["soy"],
        "dietary_restrictions": [],
        "budget_per_person": 55,
        "cuisine_preferences": ["Cantonese", "Fujian"],
        "dislikes": ["chicken feet", "jellyfish"]
    },
    {
        "id": 6,
        "name": "Frank",
        "allergies": [],
        "dietary_restrictions": ["vegan"],
        "budget_per_person": 35,
        "cuisine_preferences": ["Sichuan", "Cantonese"],
        "dislikes": ["mock meats"]
    },
    {
        "id": 7,
        "name": "Grace",
        "allergies": ["gluten"],
        "dietary_restrictions": [],
        "budget_per_person": 70,
        "cuisine_preferences": ["Cantonese", "Shanghai"],
        "dislikes": ["century eggs"]
    },
    {
        "id": 8,
        "name": "Henry",
        "allergies": [],
        "dietary_restrictions": [],
        "budget_per_person": 50,
        "cuisine_preferences": ["Sichuan", "Hunan", "Xinjiang"],
        "dislikes": ["fish with bones"]
    },
    {
        "id": 9,
        "name": "Iris",
        "allergies": ["dairy"],
        "dietary_restrictions": ["pescatarian"],
        "budget_per_person": 45,
        "cuisine_preferences": ["Cantonese", "Fujian"],
        "dislikes": ["offal"]
    },
    {
        "id": 10,
        "name": "Jack",
        "allergies": [],
        "dietary_restrictions": [],
        "budget_per_person": 40,
        "cuisine_preferences": ["Beijing", "Shanghai"],
        "dislikes": ["spicy food", "fermented foods"]
    }
]

# ============================================================================
# AI-GENERATED MENU
# ============================================================================

menu = {
    "total_cost_per_person": 35,
    "dishes": [
        {
            "name": "Steamed Jasmine Rice",
            "cuisine": "Cantonese",
            "category": "staple",
            "price": 20,
            "serves": 10,
            "allergens": [],
            "contains_meat": False,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": False,
            "contains_dairy": False,
            "halal": True,
            "vegan": True,
            "ingredients": ["rice", "water"]
        },
        {
            "name": "Mapo Tofu (Mild)",
            "cuisine": "Sichuan",
            "category": "main",
            "price": 35,
            "serves": 4,
            "allergens": ["soy"],
            "contains_meat": True,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": True,
            "contains_dairy": False,
            "halal": False,
            "vegan": False,
            "ingredients": ["tofu", "ground pork", "fermented bean paste", "scallions"]
        },
        {
            "name": "Stir-Fried Bok Choy with Garlic",
            "cuisine": "Cantonese",
            "category": "vegetable",
            "price": 28,
            "serves": 6,
            "allergens": [],
            "contains_meat": False,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": False,
            "contains_dairy": False,
            "halal": True,
            "vegan": True,
            "ingredients": ["bok choy", "garlic", "vegetable oil", "salt"]
        },
        {
            "name": "Kung Pao Chicken (Mild)",
            "cuisine": "Sichuan",
            "category": "main",
            "price": 42,
            "serves": 5,
            "allergens": ["peanuts", "soy"],
            "contains_meat": True,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": True,
            "contains_dairy": False,
            "halal": True,
            "vegan": False,
            "ingredients": ["chicken", "peanuts", "bell peppers", "soy sauce", "dried chilies"]
        },
        {
            "name": "Steamed Fish with Ginger",
            "cuisine": "Cantonese",
            "category": "main",
            "price": 55,
            "serves": 6,
            "allergens": ["soy"],
            "contains_meat": False,
            "contains_seafood": True,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": True,
            "contains_dairy": False,
            "halal": True,
            "vegan": False,
            "ingredients": ["sea bass", "ginger", "scallions", "soy sauce"]
        },
        {
            "name": "Buddha's Delight (Mixed Vegetables)",
            "cuisine": "Cantonese",
            "category": "vegetable",
            "price": 32,
            "serves": 6,
            "allergens": [],
            "contains_meat": False,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": False,
            "contains_dairy": False,
            "halal": True,
            "vegan": True,
            "ingredients": ["shiitake mushrooms", "snow peas", "carrots", "baby corn", "bamboo shoots"]
        },
        {
            "name": "Braised Lamb with Cumin",
            "cuisine": "Xinjiang",
            "category": "main",
            "price": 48,
            "serves": 5,
            "allergens": [],
            "contains_meat": True,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": False,
            "contains_dairy": False,
            "halal": True,
            "vegan": False,
            "ingredients": ["lamb", "cumin", "onions", "bell peppers"]
        },
        {
            "name": "Stir-Fried Green Beans",
            "cuisine": "Sichuan",
            "category": "vegetable",
            "price": 26,
            "serves": 6,
            "allergens": [],
            "contains_meat": False,
            "contains_seafood": False,
            "is_spicy": False,
            "contains_gluten": False,
            "contains_soy": False,
            "contains_dairy": False,
            "halal": True,
            "vegan": True,
            "ingredients": ["green beans", "garlic", "vegetable oil"]
        }
    ]
}

# ============================================================================
# VALIDATION LOGIC
# ============================================================================

def check_dish_safety(diner, dish):
    """
    Checks if a dish is safe for a diner (passes all hard constraints).
    Returns (is_safe, violations_list)
    """
    violations = []

    # Check allergies
    for allergen in diner["allergies"]:
        if allergen.lower() in [a.lower() for a in dish["allergens"]]:
            violations.append(f"Contains allergen: {allergen}")

    # Check dietary restrictions
    for restriction in diner["dietary_restrictions"]:
        if restriction == "vegetarian":
            if dish["contains_meat"] or dish["contains_seafood"]:
                violations.append(f"Not vegetarian (contains {'meat' if dish['contains_meat'] else 'seafood'})")
        elif restriction == "vegan":
            if not dish.get("vegan", False):
                violations.append(f"Not vegan")
        elif restriction == "halal":
            if not dish.get("halal", False):
                violations.append(f"Not halal")
        elif restriction == "pescatarian":
            if dish["contains_meat"]:
                violations.append(f"Contains meat (not pescatarian)")

    return len(violations) == 0, violations

def check_dish_preference(diner, dish):
    """
    Checks if a dish matches diner preferences (soft constraints).
    Returns (is_preferred, issues_list)
    """
    issues = []

    # Check dislikes
    for dislike in diner["dislikes"]:
        dislike_lower = dislike.lower()
        dish_name_lower = dish["name"].lower()
        dish_ingredients_lower = [i.lower() for i in dish["ingredients"]]

        if (dislike_lower in dish_name_lower or
            any(dislike_lower in ing for ing in dish_ingredients_lower)):
            issues.append(f"Contains disliked item: {dislike}")

        # Special cases
        if dislike == "spicy food" and dish["is_spicy"]:
            issues.append(f"Is spicy")
        elif dislike == "fermented foods" and any("fermented" in ing.lower() for ing in dish["ingredients"]):
            issues.append(f"Contains fermented ingredients")

    return len(issues) == 0, issues

def validate_diner(diner, menu):
    """
    Validates menu against one diner's constraints and preferences.

    Hard constraint test: Must have at least one safe dish in each category:
    - 1 safe staple
    - 1 safe main/protein
    - 1 safe vegetable
    And total cost must be within budget.
    """
    result = {
        "diner_id": diner["id"],
        "diner_name": diner["name"],
        "safe_to_eat": False,
        "satisfied": False,
        "hard_constraint_violations": [],
        "soft_constraint_violations": [],
        "must_avoid": [],
        "safe_meal_path": {
            "staple": None,
            "main": None,
            "vegetable": None
        },
        "safe_dishes_by_category": {
            "staple": [],
            "main": [],
            "vegetable": []
        },
        "preferred_dishes": [],
        "budget_check": None
    }

    # Check budget (hard constraint)
    if menu["total_cost_per_person"] > diner["budget_per_person"]:
        result["hard_constraint_violations"].append(
            f"Budget exceeded: ${menu['total_cost_per_person']} > ${diner['budget_per_person']}"
        )
    result["budget_check"] = f"${menu['total_cost_per_person']} ≤ ${diner['budget_per_person']}"

    # Analyze each dish
    for dish in menu["dishes"]:
        is_safe, violations = check_dish_safety(diner, dish)
        is_preferred, issues = check_dish_preference(diner, dish)

        if not is_safe:
            # Mark as must avoid with reasons
            result["must_avoid"].append({
                "dish": dish["name"],
                "reasons": violations
            })
        else:
            # Categorize safe dishes
            category = dish.get("category", "other")
            if category in result["safe_dishes_by_category"]:
                result["safe_dishes_by_category"][category].append(dish["name"])

            # Track preferred dishes
            if is_preferred:
                result["preferred_dishes"].append(dish["name"])
            else:
                # Track soft violations only for safe dishes
                for issue in issues:
                    result["soft_constraint_violations"].append(f"{dish['name']}: {issue}")

    # Check if there's a valid meal path (hard constraint)
    has_staple = len(result["safe_dishes_by_category"]["staple"]) > 0
    has_main = len(result["safe_dishes_by_category"]["main"]) > 0
    has_vegetable = len(result["safe_dishes_by_category"]["vegetable"]) > 0

    if not has_staple:
        result["hard_constraint_violations"].append("No safe staple dish available")
    else:
        result["safe_meal_path"]["staple"] = result["safe_dishes_by_category"]["staple"][0]

    if not has_main:
        result["hard_constraint_violations"].append("No safe main/protein dish available")
    else:
        result["safe_meal_path"]["main"] = result["safe_dishes_by_category"]["main"][0]

    if not has_vegetable:
        result["hard_constraint_violations"].append("No safe vegetable dish available")
    else:
        result["safe_meal_path"]["vegetable"] = result["safe_dishes_by_category"]["vegetable"][0]

    # Determine if safe to eat (passes all hard constraints)
    result["safe_to_eat"] = len(result["hard_constraint_violations"]) == 0

    # Determine if satisfied (safe + reasonable preference match)
    # Require at least 4 preferred dishes (including meal path)
    if result["safe_to_eat"]:
        if len(result["preferred_dishes"]) >= 4:
            result["satisfied"] = True
        else:
            result["soft_constraint_violations"].append(
                f"Only {len(result['preferred_dishes'])} preferred dishes (want ≥4 for satisfaction)"
            )

    return result

def calculate_fairness_score(validation_results):
    """
    Calculates a 0-100 fairness score based on validation results.

    Scoring:
    - All diners safe to eat: 60 points baseline
    - Each satisfied diner: +4 points (max 40)
    - Penalty for hard constraint violations: -15 per violation
    - Penalty for soft constraint violations: -1 per violation
    """
    total_diners = len(validation_results)
    safe_count = sum(1 for r in validation_results if r["safe_to_eat"])
    satisfied_count = sum(1 for r in validation_results if r["satisfied"])

    hard_violations = sum(len(r["hard_constraint_violations"]) for r in validation_results)
    soft_violations = sum(len(r["soft_constraint_violations"]) for r in validation_results)

    # Base score: safety is paramount
    if safe_count == total_diners:
        score = 60
    else:
        score = (safe_count / total_diners) * 60

    # Add satisfaction bonus
    score += (satisfied_count / total_diners) * 40

    # Apply penalties
    score -= hard_violations * 15
    score -= soft_violations * 1

    # Clamp to 0-100
    score = max(0, min(100, score))

    return {
        "score": round(score, 1),
        "safe_count": safe_count,
        "satisfied_count": satisfied_count,
        "total_diners": total_diners,
        "hard_violations": hard_violations,
        "soft_violations": soft_violations
    }

# ============================================================================
# MAIN EXECUTION
# ============================================================================

def main():
    print("=" * 80)
    print("CHINESE-STYLE SHARED DINNER FAIRNESS ANALYSIS")
    print("=" * 80)
    print()

    # 1. Display preference table
    preference_data = []
    for diner in diners:
        preference_data.append({
            "ID": diner["id"],
            "Name": diner["name"],
            "Allergies": ", ".join(diner["allergies"]) if diner["allergies"] else "None",
            "Dietary Restrictions": ", ".join(diner["dietary_restrictions"]) if diner["dietary_restrictions"] else "None",
            "Budget ($/person)": diner["budget_per_person"],
            "Cuisine Preferences": ", ".join(diner["cuisine_preferences"]),
            "Dislikes": ", ".join(diner["dislikes"])
        })

    df_preferences = pd.DataFrame(preference_data)
    print("DINER PREFERENCE TABLE")
    print("-" * 80)
    print(df_preferences.to_string(index=False))
    print("\n")

    # Export preferences
    df_preferences.to_csv("diner_preferences.csv", index=False)
    print("✓ Exported: diner_preferences.csv\n")

    # 2. Display menu
    print("=" * 80)
    print("AI-GENERATED MENU")
    print("=" * 80)
    print(f"Cost per person: ${menu['total_cost_per_person']}\n")

    menu_data = []
    for dish in menu["dishes"]:
        menu_data.append({
            "Dish": dish["name"],
            "Category": dish.get("category", "other").title(),
            "Cuisine": dish["cuisine"],
            "Price": f"${dish['price']}",
            "Allergens": ", ".join(dish["allergens"]) if dish["allergens"] else "None"
        })

    df_menu = pd.DataFrame(menu_data)
    print(df_menu.to_string(index=False))
    print("\n")

    # 3. Validate each diner
    print("=" * 80)
    print("DINER-BY-DINER VALIDATION")
    print("=" * 80)
    print()

    validation_results = []
    for diner in diners:
        result = validate_diner(diner, menu)
        validation_results.append(result)

        print(f"{'─' * 80}")
        print(f"DINER #{diner['id']}: {diner['name']}")
        print(f"{'─' * 80}")
        print(f"Budget Check: {result['budget_check']}")
        print(f"Safe to Eat: {'✓ YES' if result['safe_to_eat'] else '✗ NO'}")
        print(f"Satisfied: {'✓ YES' if result['satisfied'] else '✗ NO'}")
        print()

        if result["must_avoid"]:
            print("⚠ MUST AVOID (Hard Constraint Violations):")
            for item in result["must_avoid"]:
                print(f"  ✗ {item['dish']}")
                for reason in item["reasons"]:
                    print(f"      → {reason}")
            print()

        if result["safe_to_eat"]:
            print("✓ SAFE MEAL PATH:")
            print(f"  Staple: {result['safe_meal_path']['staple']}")
            print(f"  Main: {result['safe_meal_path']['main']}")
            print(f"  Vegetable: {result['safe_meal_path']['vegetable']}")
            print()

            print(f"All Safe Dishes:")
            for category in ["staple", "main", "vegetable"]:
                dishes = result["safe_dishes_by_category"][category]
                if dishes:
                    print(f"  {category.title()}: {', '.join(dishes)}")
            print()
        else:
            print("✗ HARD CONSTRAINT FAILURES:")
            for violation in result["hard_constraint_violations"]:
                print(f"  ✗ {violation}")
            print()

        if result["soft_constraint_violations"]:
            print("⚠ Soft Constraint Issues (safe but not preferred):")
            for violation in result["soft_constraint_violations"]:
                print(f"  • {violation}")
            print()

        print(f"Preferred Dishes ({len(result['preferred_dishes'])}): {', '.join(result['preferred_dishes']) if result['preferred_dishes'] else 'None'}")
        print()

    # 4. Calculate fairness score
    print("=" * 80)
    print("FAIRNESS EVALUATION")
    print("=" * 80)
    print()

    fairness = calculate_fairness_score(validation_results)

    print(f"Safe to Eat: {fairness['safe_count']}/{fairness['total_diners']} diners")
    print(f"Satisfied: {fairness['satisfied_count']}/{fairness['total_diners']} diners")
    print(f"Hard Constraint Violations: {fairness['hard_violations']}")
    print(f"Soft Constraint Violations: {fairness['soft_violations']}")
    print()
    print(f"{'═' * 80}")
    print(f"FAIRNESS SCORE: {fairness['score']}/100")
    print(f"{'═' * 80}")
    print()

    # 5. Nobody is disadvantaged?
    nobody_disadvantaged = fairness['safe_count'] == fairness['total_diners']
    print(f"Does this menu achieve \"nobody is disadvantaged\"?")
    print(f"{'✓ YES' if nobody_disadvantaged else '✗ NO'}")
    print()

    if nobody_disadvantaged:
        print("All diners have a safe, clearly identified meal path:")
        print("  - At least one safe staple dish")
        print("  - At least one safe main/protein dish")
        print("  - At least one safe vegetable dish")
        print("  - Within individual budget")
    else:
        print("The following diners are disadvantaged (no safe meal path):")
        for result in validation_results:
            if not result["safe_to_eat"]:
                print(f"\n  ✗ {result['diner_name']}:")
                for violation in result["hard_constraint_violations"]:
                    print(f"      - {violation}")
    print()

    # 6. Export full results
    evaluation_data = {
        "timestamp": datetime.now().isoformat(),
        "menu": menu,
        "diners": diners,
        "validation_results": validation_results,
        "fairness_score": fairness,
        "nobody_disadvantaged": nobody_disadvantaged
    }

    with open("menu_evaluation_results.json", "w") as f:
        json.dump(evaluation_data, f, indent=2)

    print("✓ Exported: menu_evaluation_results.json")

    # 7. Create summary CSV
    summary_data = []
    for result in validation_results:
        summary_data.append({
            "Diner": result["diner_name"],
            "Safe to Eat": "Yes" if result["safe_to_eat"] else "No",
            "Satisfied": "Yes" if result["satisfied"] else "No",
            "Hard Violations": len(result["hard_constraint_violations"]),
            "Soft Violations": len(result["soft_constraint_violations"]),
            "Must Avoid": len(result["must_avoid"]),
            "Preferred Dishes": len(result["preferred_dishes"])
        })

    df_summary = pd.DataFrame(summary_data)
    df_summary.to_csv("validation_summary.csv", index=False)
    print("✓ Exported: validation_summary.csv")
    print()

if __name__ == "__main__":
    main()
