"""
Smart unfavoriting: Remove properties that don't meet minimum criteria thresholds
"""
import os
import asyncio
import argparse
import pandas as pd
from playwright.async_api import async_playwright

AUTH_JSON = "auth.json"
ENRICHED_CSV = "analysis_output.csv"
SCORE_WEIGHTS = (0.6, 0.4)  # GPT vs Custom weighting

# Default minimum thresholds for each criterion
DEFAULT_THRESHOLDS = {
    'market_garden': 3,           # Minimum 3/5 for market garden
    'guest_accommodation': 3,      # Minimum 3/5 for guest accommodation
    'workshop': 2,                 # Minimum 2/5 for workshop
    'rental_units': 3,             # Minimum 3/5 for rental potential
    'location': 3,                 # Minimum 3/5 for location
    'local_market': 3,             # Minimum 3/5 for market access
    'overall_score': 0.0,          # Minimum weighted score
    'max_risk': 'Gemiddeld'        # Maximum acceptable risk (Laag/Gemiddeld/Hoog)
}

def load_properties_with_criteria():
    """Load properties with parsed criteria"""
    if not os.path.exists(ENRICHED_CSV):
        print(f"❌ {ENRICHED_CSV} not found. Run 'parse_criteria.py' first.")
        return None

    df = pd.read_csv(ENRICHED_CSV)

    # Check if criteria columns exist
    required_cols = ['market_garden', 'guest_accommodation', 'workshop',
                    'rental_units', 'location', 'local_market']
    missing_cols = [col for col in required_cols if col not in df.columns]
    if missing_cols:
        print(f"❌ Missing columns: {', '.join(missing_cols)}. Run 'parse_criteria.py' first.")
        return None

    # Quick readiness snapshot
    total = len(df)
    missing_combined = df['overall_score_combined'].isna().sum() if 'overall_score_combined' in df.columns else total
    missing_risk = df['risk_profile'].isna().sum() if 'risk_profile' in df.columns else total

    print("\n📊 Data readiness:")
    if 'overall_score_combined' in df.columns:
        print(f"  Combined score coverage: {total - missing_combined}/{total}")
    else:
        print("  Combined score coverage: 0 (column missing, will fallback to weighted GPT/custom)")
    print(f"  Risk profiles: {total - missing_risk}/{total}")

    return df


def safe_float(value):
    """Convert to float, returning None on blanks/NaN."""
    try:
        if value is None:
            return None
        if isinstance(value, str) and not value.strip():
            return None
        fval = float(value)
        if pd.isna(fval):
            return None
        return fval
    except Exception:
        return None


def get_risk_factor(risk_profile: str):
    """Convert risk profile string to multiplier."""
    if not risk_profile:
        return 0.9
    risk_profile = str(risk_profile).lower()
    if 'laag' in risk_profile:
        return 1.0
    if 'gemiddeld' in risk_profile:
        return 0.9
    if 'hoog' in risk_profile:
        return 0.7
    return 0.9


def resolve_overall_score(row):
    """
    Pick the most reliable overall score available and risk-adjust if needed.
    Preference: combined column -> stored overall_score -> computed weighted from GPT/custom.
    """
    combined = safe_float(row.get('overall_score_combined') or row.get('overall_score'))
    gpt_score = safe_float(row.get('Gewogen Score'))
    custom_score = safe_float(row.get('custom_overall_score'))
    risk_factor = get_risk_factor(row.get('risk_profile'))

    if combined is not None:
        return combined, "combined"

    if gpt_score is None and custom_score is None:
        return None, "missing"

    if gpt_score is None:
        return round(custom_score * risk_factor, 2), "custom_only_risk_adjusted"

    if custom_score is None:
        return round(gpt_score * risk_factor, 2), "gpt_only_risk_adjusted"

    weighted = (gpt_score * SCORE_WEIGHTS[0] + custom_score * SCORE_WEIGHTS[1]) * risk_factor
    return round(weighted, 2), "computed_weighted"

def filter_properties_by_criteria(df, thresholds, require_all=False):
    """
    Filter properties based on criteria thresholds

    Args:
        df: DataFrame with property data
        thresholds: Dict of minimum thresholds for each criterion
        require_all: If True, ALL criteria must meet threshold (AND logic)
                    If False, AT LEAST ONE criterion must meet threshold (OR logic)
    """
    to_unfavorite = []
    skipped = []

    for idx, row in df.iterrows():
        url = row['URL']
        overall_score, score_source = resolve_overall_score(row)
        if overall_score is None:
            skipped.append({'url': url, 'reason': 'Missing overall score'})
            continue
        risk = row.get('risk_profile', None)

        # Check overall score
        if overall_score < thresholds['overall_score']:
            to_unfavorite.append({
                'url': url,
                'reason': f"Overall score {overall_score:.2f} < {thresholds['overall_score']} ({score_source})",
                'overall_score': overall_score
            })
            continue

        # Check risk profile
        risk_levels = {'Laag': 1, 'Gemiddeld': 2, 'Hoog': 3}
        max_risk_level = risk_levels.get(thresholds['max_risk'], 3)
        if risk and risk_levels.get(risk, 3) > max_risk_level:
            to_unfavorite.append({
                'url': url,
                'reason': f"Risk '{risk}' > max '{thresholds['max_risk']}'",
                'overall_score': overall_score
            })
            continue

        # Check individual criteria
        criteria_met = []
        criteria_failed = []

        for criterion in ['market_garden', 'guest_accommodation', 'workshop',
                         'rental_units', 'location', 'local_market']:
            score = row.get(criterion, 0)
            threshold = thresholds.get(criterion, 1)

            if pd.isna(score):
                criteria_failed.append(f"{criterion}: N/A")
            elif score >= threshold:
                criteria_met.append(criterion)
            else:
                criteria_failed.append(f"{criterion}: {score}/{threshold}")

        # Apply logic
        if require_all:
            # ALL criteria must be met
            if criteria_failed:
                to_unfavorite.append({
                    'url': url,
                    'reason': f"Failed: {', '.join(criteria_failed[:3])}",
                    'overall_score': overall_score
                })
        else:
            # At least ONE criterion must be met
            if not criteria_met:
                to_unfavorite.append({
                    'url': url,
                    'reason': f"No criteria met minimum thresholds",
                    'overall_score': overall_score
                })

    return to_unfavorite, skipped

async def unfavorite_properties(urls_to_remove, dry_run=False):
    """Remove properties from Properstar favorites"""

    if dry_run:
        print("\n🔍 DRY RUN - These properties would be unfavorited:\n")
        for i, item in enumerate(urls_to_remove[:20], 1):
            print(f"{i}. {item['url']}")
            print(f"   Score: {item['overall_score']:.2f} | Reason: {item['reason']}")
        if len(urls_to_remove) > 20:
            print(f"\n... and {len(urls_to_remove) - 20} more properties")
        print("\n💡 Run without --dry-run to actually remove these favorites")
        return

    # Confirmation
    print(f"\n⚠️  WARNING: About to unfavorite {len(urls_to_remove)} properties!")
    print("This action cannot be undone from this script.\n")

    for i, item in enumerate(urls_to_remove[:10], 1):
        print(f"{i}. Score: {item['overall_score']:.2f} | {item['reason']}")
    if len(urls_to_remove) > 10:
        print(f"... and {len(urls_to_remove) - 10} more")

    confirm = input("\n⚠️  Type 'UNFAVORITE' to proceed: ")
    if confirm.strip() != "UNFAVORITE":
        print("❌ Cancelled")
        return

    # Execute unfavoriting
    async with async_playwright() as p:
        print("\n🌐 Opening browser...")
        browser = await p.chromium.launch(headless=False)

        if os.path.exists(AUTH_JSON):
            context = await browser.new_context(storage_state=AUTH_JSON)
        else:
            print("⚠️  No auth.json found. Please log in manually.")
            context = await browser.new_context()

        page = await context.new_page()

        success_count = 0
        failed_urls = []

        for i, item in enumerate(urls_to_remove, 1):
            url = item['url']
            try:
                print(f"[{i}/{len(urls_to_remove)}] Processing: {url}")
                await page.goto(url, timeout=15000)
                await page.wait_for_timeout(1000)

                # Try to find and click the favorite button
                favorite_btn = await page.query_selector("button[aria-label='Favoriet']")
                if favorite_btn:
                    # Check if it's currently favorited (usually has a specific class or state)
                    await favorite_btn.click()
                    print(f"  ✓ Unfavorited")
                    success_count += 1
                    await asyncio.sleep(1)
                else:
                    print(f"  ⚠️  Favorite button not found")
                    failed_urls.append(url)

            except Exception as e:
                print(f"  ❌ Error: {e}")
                failed_urls.append(url)

        await browser.close()

        print("\n" + "=" * 60)
        print("📊 UNFAVORITE SUMMARY")
        print("=" * 60)
        print(f"✅ Successfully unfavorited: {success_count}")
        print(f"❌ Failed: {len(failed_urls)}")
        print("=" * 60)

        if failed_urls:
            print("\n❌ Failed URLs:")
            for url in failed_urls[:10]:
                print(f"  • {url}")

async def main():
    parser = argparse.ArgumentParser(
        description="Smart unfavorite: Remove properties below criteria thresholds"
    )
    parser.add_argument("--dry-run", action="store_true",
                       help="Preview what would be removed without making changes")
    parser.add_argument("--overall-score", type=float, default=DEFAULT_THRESHOLDS['overall_score'],
                       help=f"Minimum overall weighted score (default: {DEFAULT_THRESHOLDS['overall_score']})")
    parser.add_argument("--market-garden", type=int, default=DEFAULT_THRESHOLDS['market_garden'],
                       help=f"Minimum market garden score (default: {DEFAULT_THRESHOLDS['market_garden']})")
    parser.add_argument("--guest", type=int, default=DEFAULT_THRESHOLDS['guest_accommodation'],
                       help=f"Minimum guest accommodation score (default: {DEFAULT_THRESHOLDS['guest_accommodation']})")
    parser.add_argument("--workshop", type=int, default=DEFAULT_THRESHOLDS['workshop'],
                       help=f"Minimum workshop score (default: {DEFAULT_THRESHOLDS['workshop']})")
    parser.add_argument("--rental", type=int, default=DEFAULT_THRESHOLDS['rental_units'],
                       help=f"Minimum rental units score (default: {DEFAULT_THRESHOLDS['rental_units']})")
    parser.add_argument("--location", type=int, default=DEFAULT_THRESHOLDS['location'],
                       help=f"Minimum location score (default: {DEFAULT_THRESHOLDS['location']})")
    parser.add_argument("--market", type=int, default=DEFAULT_THRESHOLDS['local_market'],
                       help=f"Minimum local market score (default: {DEFAULT_THRESHOLDS['local_market']})")
    parser.add_argument("--max-risk", type=str, default=DEFAULT_THRESHOLDS['max_risk'],
                       choices=['Laag', 'Gemiddeld', 'Hoog'],
                       help=f"Maximum acceptable risk level (default: {DEFAULT_THRESHOLDS['max_risk']})")
    parser.add_argument("--require-all", action="store_true",
                       help="Require ALL criteria to be met (default: at least ONE criterion)")

    args = parser.parse_args()

    print("=" * 60)
    print("🎯 SMART UNFAVORITE")
    print("=" * 60)

    # Load data
    df = load_properties_with_criteria()
    if df is None:
        return

    # Set thresholds from arguments
    thresholds = {
        'overall_score': args.overall_score,
        'market_garden': args.market_garden,
        'guest_accommodation': args.guest,
        'workshop': args.workshop,
        'rental_units': args.rental,
        'location': args.location,
        'local_market': args.market,
        'max_risk': args.max_risk
    }

    print("\n📊 Filtering Criteria:")
    print(f"  Overall Score: ≥ {thresholds['overall_score']}")
    print(f"  Market Garden: ≥ {thresholds['market_garden']}/5")
    print(f"  Guest Accommodation: ≥ {thresholds['guest_accommodation']}/5")
    print(f"  Workshop: ≥ {thresholds['workshop']}/5")
    print(f"  Rental Units: ≥ {thresholds['rental_units']}/5")
    print(f"  Location: ≥ {thresholds['location']}/5")
    print(f"  Local Market: ≥ {thresholds['local_market']}/5")
    print(f"  Max Risk: {thresholds['max_risk']}")
    print(f"  Logic: {'ALL criteria must be met' if args.require_all else 'At least ONE criterion must be met'}")
    print()

    # Filter properties
    to_unfavorite, skipped = filter_properties_by_criteria(df, thresholds, args.require_all)

    if skipped:
        print(f"ℹ️  Skipped {len(skipped)} properties due to missing scores. Run parse_criteria.py to fill gaps.")

    if not to_unfavorite:
        print("✅ No properties to unfavorite. All properties meet the criteria!")
        return

    print(f"🗑️  Found {len(to_unfavorite)} properties to unfavorite")

    # Execute unfavoriting
    await unfavorite_properties(to_unfavorite, dry_run=args.dry_run)

if __name__ == "__main__":
    asyncio.run(main())
