import frappe
import re
from frappe.utils import now, now_datetime

def get_pricing_config():
    if not frappe.db.exists("Pricing Score Config", "Pricing Score Config"):
        return None
    return frappe.get_doc("Pricing Score Config", "Pricing Score Config")

def parse_ram(ram_str):
    if not ram_str:
        return 0
    m = re.search(r'(\d+(?:\.\d+)?)', str(ram_str))
    if m:
        return int(float(m.group(1)))
    return 0

def parse_cpu(cpu_str):
    if not cpu_str:
        return {"brand": "", "series": "", "gen": 0}
    cpu = str(cpu_str).lower()
    result = {"brand": "", "series": "", "gen": 0}
    if "intel" in cpu or "core" in cpu or "i3" in cpu or "i5" in cpu or "i7" in cpu or "i9" in cpu or "xeon" in cpu:
        result["brand"] = "intel"
    elif "ryzen" in cpu or "athlon" in cpu or "amd" in cpu:
        result["brand"] = "amd"
    elif "apple" in cpu or "m1" in cpu or "m2" in cpu or "m3" in cpu:
        result["brand"] = "apple"
    series_match = re.search(r'(i[3579])', cpu)
    if series_match:
        result["series"] = series_match.group(1)
    elif "xeon" in cpu:
        result["series"] = "xeon"
    elif "ryzen" in cpu:
        ryz_match = re.search(r'ryzen\s+(\d+)', cpu)
        if ryz_match:
            result["series"] = f"r{ryz_match.group(1)}"
    elif "m1" in cpu:
        result["series"] = "m1"
    elif "m2" in cpu:
        result["series"] = "m2"
    elif "m3" in cpu:
        result["series"] = "m3"
    gen_match = re.search(r'(?:i[3579]-|core\s+i[3579]\s+)(\d)', cpu)
    if gen_match:
        first_digit = int(gen_match.group(1))
        if first_digit == 1:
            gen_match2 = re.search(r'(?:i[3579]-|core\s+i[3579]\s+)1(\d)', cpu)
            if gen_match2:
                result["gen"] = 10 + int(gen_match2.group(1))
            else:
                result["gen"] = 1
        else:
            result["gen"] = first_digit
    gen_match3 = re.search(r'(?:i[3579]-)(\d{4})', cpu)
    if gen_match3:
        model = gen_match3.group(1)
        first_digit = int(model[0])
        if first_digit == 1:
            result["gen"] = 10 + int(model[1])
        else:
            result["gen"] = first_digit
    return result

@frappe.whitelist()
def calculate_serial_score(serial_no):
    serial = frappe.get_doc("Serial No", serial_no)
    config = get_pricing_config()
    if not config:
        return {"error": "No Pricing Score Config found"}
    
    score = 0.0
    details = []
    
    cos_grade = serial.get("cosmetic_grade", "")
    if cos_grade:
        grade_match = re.search(r'[Cc](\d+)', str(cos_grade))
        if grade_match:
            g = int(grade_match.group(1))
            if g <= 2:
                return {"status": "scrap", "reason": f"Cosmetic C{g} - below minimum"}
            base = getattr(config, f"c{g}_base", 0)
            score += base
            details.append(f"Cosmetic C{g} = +{base}")
    
    cpu_info = parse_cpu(serial.get("processor", ""))
    if cpu_info["series"] in ("i7", "r7"):
        score += config.i7_bonus
        details.append(f"CPU {cpu_info['series'].upper()} = +{config.i7_bonus}")
    elif cpu_info["series"] in ("i9", "r9"):
        score += config.i9_bonus
        details.append(f"CPU {cpu_info['series'].upper()} = +{config.i9_bonus}")
    
    gen = cpu_info["gen"]
    gen_bonus = 0
    if gen == 10: gen_bonus = config.gen_10_bonus
    elif gen == 11: gen_bonus = config.gen_11_bonus
    elif gen == 12: gen_bonus = config.gen_12_bonus
    elif gen == 13: gen_bonus = config.gen_13_bonus
    elif gen == 14: gen_bonus = config.gen_14_bonus
    if gen_bonus > 0:
        score += gen_bonus
        details.append(f"Gen {gen} = +{gen_bonus}")
    
    ram_gb = parse_ram(serial.get("ram", ""))
    if ram_gb >= 16:
        score += config.ram_16_bonus
        details.append(f"RAM {ram_gb}GB = +{config.ram_16_bonus}")
    if ram_gb >= 32:
        score += config.ram_32_bonus
        details.append(f"RAM {ram_gb}GB = +{config.ram_32_bonus}")
    if ram_gb > 32:
        details.append(f"WARNING: {ram_gb}GB exceeds laptop/desktop norm")
    
    if score >= config.medium_threshold:
        tier = "High"
    elif score >= config.low_threshold:
        tier = "Medium"
    else:
        tier = "Low"
    
    market = {"low": 0, "median": 0, "high": 0}
    age_days = 0
    age_status = "unknown"
    
    if serial.item_code:
        item = frappe.get_doc("Item", serial.item_code)
        market = {
            "low": item.market_low or item.base_market_price or 0,
            "median": item.market_median or item.base_market_price or 0,
            "high": item.market_high or item.base_market_price or 0,
        }
        if item.market_last_priced:
            age_days = (now_datetime() - item.market_last_priced).days
            if age_days <= 30: age_status = "current"
            elif age_days <= 60: age_status = "stale"
            elif age_days <= 90: age_status = "aging"
            else: age_status = "expired"
    
    suggested_price = market["high"] if tier == "High" else market["median"] if tier == "Medium" else market["low"]
    
    serial.desirability_score = round(score, 1)
    serial.suggested_tier = tier
    serial.suggested_price = suggested_price
    serial.save()
    frappe.db.commit()
    
    return {
        "status": "ok",
        "serial_no": serial_no,
        "score": round(score, 1),
        "tier": tier,
        "details": details,
        "market_prices": market,
        "suggested_price": suggested_price,
        "age_days": age_days,
        "age_status": age_status,
    }

@frappe.whitelist()
def batch_calculate_scores(batch_size=100):
    serials = frappe.get_all("Serial No",
        filters={"cosmetic_grade": ["is", "set"]},
        fields=["name"],
        limit=int(batch_size)
    )
    results = {"updated": 0, "scrap": 0, "errors": 0}
    for s in serials:
        try:
            result = calculate_serial_score(s.name)
            if result.get("status") == "scrap":
                results["scrap"] += 1
            else:
                results["updated"] += 1
        except Exception as e:
            results["errors"] += 1
            frappe.log_error(f"Score calc error for {s.name}: {e}")
    return results

@frappe.whitelist()
def get_sales_pricing_data(limit=50):
    """Get pricing data for Sales Manager page."""
    config = get_pricing_config()
    if not config:
        return {"error": "No Pricing Score Config"}
    
    serials = frappe.get_all("Serial No",
        filters={"cosmetic_grade": ["is", "set"]},
        fields=["name", "serial_no", "item_code", "item_name", "cosmetic_grade", "processor", "ram", "desirability_score", "suggested_tier", "suggested_price", "assigned_price", "pricing_status"],
        limit=int(limit),
        order_by="modified desc"
    )
    
    results = []
    for s in serials:
        age = {"days": 0, "status": "unknown", "color": "gray"}
        market = {"low": 0, "median": 0, "high": 0}
        
        if s.item_code:
            item = frappe.get_doc("Item", s.item_code)
            market = {
                "low": item.market_low or 0,
                "median": item.market_median or 0,
                "high": item.market_high or 0,
            }
            if item.market_last_priced:
                age_days = (now_datetime() - item.market_last_priced).days
                age["days"] = age_days
                if age_days <= 30:
                    age["status"] = "current"
                    age["color"] = "green"
                elif age_days <= 60:
                    age["status"] = "stale"
                    age["color"] = "yellow"
                elif age_days <= 90:
                    age["status"] = "aging"
                    age["color"] = "orange"
                else:
                    age["status"] = "expired"
                    age["color"] = "red"
        
        results.append({
            "serial_no": s.name,
            "item_code": s.item_code,
            "item_name": s.item_name,
            "cosmetic_grade": s.cosmetic_grade,
            "processor": s.processor,
            "ram": s.ram,
            "score": s.desirability_score,
            "tier": s.suggested_tier,
            "market": market,
            "suggested_price": s.suggested_price,
            "assigned_price": s.assigned_price,
            "pricing_status": s.pricing_status,
            "age": age,
        })
    
    return {
        "serials": results,
        "config": {
            "low_threshold": config.low_threshold,
            "medium_threshold": config.medium_threshold,
        }
    }
