import asyncio
import json
from features.base_feature import BaseFeature
from tools.tavily_search import TavilySearchTool
from tools.calculator import CalculatorTool

_tavily = TavilySearchTool()
_calc = CalculatorTool()


class TripFeature(BaseFeature):

    async def get_context(self, preferences_a: dict, preferences_b: dict, user_a_id: int = None, user_b_id: int = None) -> str:
        """
        Compute date intersection across both parties.
        Fetch real destination options via Tavily.
        """
        raw_a = preferences_a.get("raw_details", {})
        raw_b = preferences_b.get("raw_details", {})

        dates_a = raw_a.get("available_dates") or raw_a.get("dates") or []
        dates_b = raw_b.get("available_dates") or raw_b.get("dates") or []
        budget_a = raw_a.get("budget_per_person") or raw_a.get("budget") or ""
        budget_b = raw_b.get("budget_per_person") or raw_b.get("budget") or ""
        dest_pref_a = raw_a.get("destination_preference") or raw_a.get("destination") or ""
        dest_pref_b = raw_b.get("destination_preference") or raw_b.get("destination") or ""
        from_city = raw_a.get("origin") or raw_b.get("origin") or raw_a.get("city") or "Mumbai"
        accom_a = raw_a.get("accommodation_type") or ""
        accom_b = raw_b.get("accommodation_type") or ""

        # Compute date overlap (simple string intersection)
        dates_a_set = set(str(d).lower() for d in (dates_a if isinstance(dates_a, list) else [dates_a]))
        dates_b_set = set(str(d).lower() for d in (dates_b if isinstance(dates_b, list) else [dates_b]))
        common_dates = dates_a_set & dates_b_set

        # Budget ceiling = lower budget
        budget_ceiling = ""
        if budget_a and budget_b:
            try:
                ba = float(str(budget_a).replace(",", ""))
                bb = float(str(budget_b).replace(",", ""))
                budget_ceiling = f"₹{min(ba, bb):,.0f}/person"
            except (ValueError, TypeError):
                budget_ceiling = f"{budget_a} or {budget_b} (take lower)"
        elif budget_a or budget_b:
            budget_ceiling = f"₹{budget_a or budget_b}/person"

        # Destination type combined
        dest_type = " or ".join(filter(None, [dest_pref_a, dest_pref_b])) or "weekend getaway"

        # Tavily: real destination options
        search_text = ""
        try:
            query = f"weekend getaway destinations from {from_city} {dest_type} budget India 2026"
            result = await _tavily.execute(query)
            answer = result.get("answer", "")
            results = result.get("results", [])[:4]
            parts = []
            if answer:
                parts.append(f"Destination summary: {answer[:300]}")
            for r in results:
                title = r.get("title", "")
                content = r.get("content", "")[:150]
                if title:
                    parts.append(f"  • {title}: {content}")
            search_text = "\n".join(parts)
        except Exception as e:
            search_text = f"Search unavailable. Use your knowledge of destinations from {from_city}."

        lines = [
            "TRIP PLANNING DOMAIN RULES:",
            "• Date overlap is PRIORITY #1 — only propose dates both parties are available.",
            "• Budget ceiling = LOWEST budget in the group. No one should overspend.",
            "• Destination must satisfy at least one preference from each party.",
            "• Accommodation type: prefer the more comfortable option if budget allows.",
            "• If no date overlap: escalate immediately with adjusted date suggestions.",
        ]
        if from_city:
            lines.append(f"\nOrigin city: {from_city}")
        if dates_a:
            lines.append(f"Person A available: {dates_a}")
        if dates_b:
            lines.append(f"Person B available: {dates_b}")
        if common_dates:
            lines.append(f"✅ OVERLAPPING DATES: {', '.join(common_dates)}")
        else:
            lines.append("⚠️ No exact date overlap found — propose closest alternatives.")
        if budget_ceiling:
            lines.append(f"Budget ceiling: {budget_ceiling}")
        if dest_pref_a:
            lines.append(f"Person A wants: {dest_pref_a}")
        if dest_pref_b:
            lines.append(f"Person B wants: {dest_pref_b}")
        if accom_a or accom_b:
            lines.append(f"Accommodation preferences: {accom_a or ''} / {accom_b or ''}")
        if search_text:
            lines.append(f"\nREAL DESTINATION OPTIONS:\n{search_text}")

        return "\n".join(lines)

    def format_resolution(
        self, resolution: dict, preferences_a: dict, preferences_b: dict
    ) -> str:
        status = resolution.get("status", "resolved")
        final = resolution.get("final_proposal", {})
        details = final.get("details", {})
        rounds = resolution.get("rounds_taken", "?")
        summary = resolution.get("summary", "")

        if status == "escalated":
            return (
                f"⚠️ *Trip Planning — Human Decision Needed*\n\n"
                f"_{summary}_\n\n"
                f"Agents proposed options but couldn't finalize in {rounds} round(s). "
                f"Please agree on dates and destination directly."
            )

        destination = details.get("destination") or details.get("place") or final.get("summary", "")
        dates = details.get("dates") or details.get("travel_dates") or details.get("date") or ""
        budget = details.get("budget_per_person") or details.get("budget") or ""
        accommodation = details.get("accommodation") or details.get("stay") or ""
        activities = details.get("activities") or details.get("things_to_do") or []
        duration = details.get("duration") or details.get("nights") or ""

        lines = ["✈️ *Trip Planned!*\n"]
        if destination:
            lines.append(f"🗺 *Destination:* {destination}")
        if dates:
            lines.append(f"📅 *Dates:* {dates}")
        if duration:
            lines.append(f"⏱ *Duration:* {duration}")
        if accommodation:
            lines.append(f"🏨 *Stay:* {accommodation}")
        if budget:
            lines.append(f"💰 *Budget/person:* ₹{budget}")
        if activities and isinstance(activities, list):
            lines.append("🎯 *Activities:*")
            for act in activities[:4]:
                lines.append(f"  • {act}")
        elif activities:
            lines.append(f"🎯 *Activities:* {activities}")
        lines.append(f"\n⏱ Planned in {rounds} round(s)")
        if summary and summary != "Agreement reached":
            lines.append(f"_{summary}_")

        return "\n".join(lines)


# ────────────────────────────────────────────────────────────────────────────
# Group negotiation for 3+ participants (trip planning)
# ────────────────────────────────────────────────────────────────────────────

async def run_group_negotiation(
    negotiation_id: str,
    all_preferences: list,
    all_user_ids: list,
    feature_type: str = "trip",
    personalities: list = None,
    on_round_update=None,
    on_resolution=None,
) -> dict:
    """
    Multi-agent group negotiation using a mediator approach.
    One NegotiatorAgent acts as mediator, sees all preferences, proposes.
    Each participant's NegotiatorAgent scores the proposal.
    Iterates up to 5 rounds; escalates if no full agreement.
    """
    import database as db
    from agents.negotiator_agent import NegotiatorAgent

    if personalities is None:
        personalities = ["balanced"] * len(all_preferences)

    await db.update_negotiation_status(negotiation_id, "active")

    # Create mediator (uses balanced personality)
    mediator = NegotiatorAgent(personality="balanced")

    # Create per-participant evaluators
    evaluators = [
        NegotiatorAgent(personality=p)
        for p in personalities
    ]

    # Pre-fetch trip context
    feature = TripFeature()
    feature_context = ""
    if len(all_preferences) >= 2:
        try:
            feature_context = await feature.get_context(all_preferences[0], all_preferences[1])
        except Exception:
            feature_context = ""

    max_rounds = 5
    current_proposal = None
    satisfaction_timeline = []

    for round_num in range(1, max_rounds + 1):
        await asyncio.sleep(1.5)

        # Mediator generates/refines proposal
        mediator_prompt = f"""You are MEDIATING a group {feature_type} negotiation with {len(all_preferences)} participants.

{"DOMAIN CONTEXT:" + chr(10) + feature_context if feature_context else ""}

ALL PARTICIPANTS' PREFERENCES:
{json.dumps(all_preferences, indent=2)}

{"PREVIOUS PROPOSAL (refine based on feedback below):" + chr(10) + json.dumps(current_proposal, indent=2) if current_proposal else ""}

Round {round_num} of {max_rounds}. Generate a proposal that maximizes GROUP satisfaction.
Rules:
- Budget ceiling = LOWEST budget among all participants.
- Dates = intersection of all available dates (or closest compromise).
- Every participant must get at least one preference honored.
- Return the standard proposal JSON format."""

        try:
            mediated = await mediator.call(user_prompt=mediator_prompt)
        except Exception:
            mediated = {
                "action": "propose",
                "proposal": {"summary": "Group proposal", "details": {}},
                "satisfaction_score": 60,
                "reasoning": "Mediator generated group proposal",
                "concessions_made": [],
                "concessions_requested": [],
            }

        current_proposal = mediated.get("proposal", {})

        # Score with each participant
        scores = []
        low_scorer = None
        low_score = 100

        for i, (prefs, evaluator) in enumerate(zip(all_preferences, evaluators)):
            eval_prompt = f"""Evaluate this group {feature_type} proposal for Participant {i+1}.

PROPOSAL:
{json.dumps(current_proposal, indent=2)}

YOUR PREFERENCES:
{json.dumps(prefs, indent=2)}

Score it and decide: accept (>= 65), counter (40-64), or escalate (< 40 after round 3).
Return standard JSON format."""
            try:
                eval_response = await evaluator.call(user_prompt=eval_prompt)
            except Exception:
                eval_response = {"action": "accept", "satisfaction_score": 65, "reasoning": ""}

            score = eval_response.get("satisfaction_score", 65)
            scores.append(score)
            if score < low_score:
                low_score = score
                low_scorer = i

        avg_score = sum(scores) / len(scores)
        sat_entry = {"round": round_num}
        for i, s in enumerate(scores):
            sat_entry[f"score_{chr(65+i)}"] = s
        satisfaction_timeline.append(sat_entry)

        round_data = {
            "negotiation_id": negotiation_id,
            "round_number": round_num,
            "action": "counter" if avg_score < 65 else "accept",
            "proposal": current_proposal,
            "satisfaction_score": avg_score,
            "reasoning": mediated.get("reasoning", "")[:200],
            "group_scores": scores,
            "satisfaction_a": scores[0] if scores else 0,
            "satisfaction_b": scores[1] if len(scores) > 1 else 0,
        }

        # Save round
        try:
            await db.save_round(
                negotiation_id=negotiation_id,
                round_number=round_num,
                proposer_id=all_user_ids[0],
                proposal=mediated,
                response_type=round_data["action"],
                reasoning=round_data["reasoning"],
                satisfaction_a=scores[0] if scores else 0,
                satisfaction_b=scores[1] if len(scores) > 1 else 0,
                concessions_made=[],
            )
        except Exception:
            pass

        if on_round_update:
            await on_round_update(round_data)

        # All participants satisfied
        if all(s >= 65 for s in scores):
            resolution = {
                "status": "resolved",
                "final_proposal": current_proposal,
                "rounds_taken": round_num,
                "summary": current_proposal.get("summary", "Group trip planned!"),
                "satisfaction_timeline": satisfaction_timeline,
                "group_scores": scores,
            }
            await db.update_negotiation_status(negotiation_id, "resolved", resolution)
            if on_resolution:
                await on_resolution(resolution)
            return resolution

        # After round 3, if any score < 40, escalate
        if round_num >= 3 and low_score < 40:
            resolution = {
                "status": "escalated",
                "final_proposal": current_proposal,
                "rounds_taken": round_num,
                "summary": f"Participant {low_scorer+1} couldn't agree. Human decision needed.",
                "satisfaction_timeline": satisfaction_timeline,
                "group_scores": scores,
            }
            await db.update_negotiation_status(negotiation_id, "escalated", resolution)
            if on_resolution:
                await on_resolution(resolution)
            return resolution

    # Max rounds exhausted
    resolution = {
        "status": "escalated",
        "final_proposal": current_proposal,
        "rounds_taken": max_rounds,
        "summary": "Max rounds reached. Best group proposal for human review.",
        "satisfaction_timeline": satisfaction_timeline,
        "group_scores": scores if scores else [],
    }
    await db.update_negotiation_status(negotiation_id, "escalated", resolution)
    if on_resolution:
        await on_resolution(resolution)
    return resolution
