import frappe
from frappe import _

# Mapping of material types to service categories
HDD_MATERIALS = {
    "Loose Hard Drive", "External Hard Drive", "Printers/Copiers Hard Drives",
    "Loose SSD or mSATA Drive", "Server", "Desktop", "Laptop", "Chromebook / Notebook",
    "All In One", "HPStream", "Thin Clients", "Tablet", "Cell Phone / Smart Phone",
    "Gaming Systems", "Smart TV", "POS", "POS Terminals", "DVR", "Switch",
    "Network / Modems / Routers", "Office/ IP Phone", "Personal Electronics / PDA",
    "CRT TV", "Printers/Copiers", "USB Drive", "SD Cards", "GPS"
}

TAPE_MATERIALS = {"CD / Floppy / DVD / Tapes"}

def _get_service_item(destruction_method, has_hardware=True):
    """Map destruction method to service item code."""
    if destruction_method == "Wipe":
        if has_hardware:
            return "SVR-HDD-WIPE-1PASS"  # Default to 1-pass; user can override
        return "SVR-HDD-WIPE-3PASS-NOHW"
    elif destruction_method in ("Shred", "Degauss"):
        if has_hardware:
            return "SVR-HDD-SERIAL-WIPE-HW"
        return "SVR-HDD-SERIAL-WIPE-NOHW"
    elif destruction_method == "None":
        return None
    return "SVR-HDD-WIPE-1PASS"

def _get_tape_item(destruction_method, has_hardware=True):
    if has_hardware:
        return "SVR-TAPE-SHRED"
    return "SVR-TAPE-SHRED-NOHW"

def _get_onsite_item(has_hardware=True):
    return "SVR-HDD-ONSITE-HW" if has_hardware else "SVR-HDD-ONSITE-NOHW"

def _calculate_tier_price(item_code, qty):
    """Return unit rate for given qty based on tier pricing."""
    tiers = {
        "SVR-HDD-WIPE-1PASS": [(1,10,7.00), (11,30,6.00), (31,50,4.50), (51,99,3.50), (100,999999,3.00)],
        "SVR-HDD-WIPE-3PASS-HW": [(1,10,8.50), (11,30,7.00), (31,50,5.25), (51,99,4.25), (100,999999,3.50)],
        "SVR-HDD-WIPE-3PASS-NOHW": [(1,10,14.00), (11,30,11.50), (31,50,8.40), (51,99,7.25), (100,999999,6.00)],
        "SVR-HDD-SERIAL-WIPE-HW": [(1,10,9.00), (11,30,7.50), (31,50,6.00), (51,99,5.00), (100,999999,4.25)],
        "SVR-HDD-SERIAL-WIPE-NOHW": [(1,10,14.50), (11,30,12.00), (31,50,10.00), (51,99,8.00), (100,999999,6.50)],
        "SVR-HDD-ONSITE-HW": [(1,100,500.00), (101,999999,3.50)],
        "SVR-HDD-ONSITE-NOHW": [(1,100,850.00), (101,999999,6.00)],
        "SVR-TAPE-SHRED": [(1,10,4.00), (11,30,3.30), (31,50,2.70), (51,99,2.00), (100,999999,1.50)],
        "SVR-TAPE-SHRED-NOHW": [(1,10,6.65), (11,30,5.50), (31,50,4.50), (51,99,3.35), (100,999999,2.50)],
        "SVR-VIDEO-RECORD": [(1,999999,3.50)],
    }
    for item, tlist in tiers.items():
        if item == item_code:
            for min_qty, max_qty, rate in tlist:
                if min_qty <= qty <= max_qty:
                    return rate
    # Fallback to Item Price
    rate = frappe.db.get_value("Item Price", {"item_code": item_code, "price_list": "2025 Service Rates", "selling": 1}, "price_list_rate")
    return rate or 0

@frappe.whitelist()
def generate_service_invoice(load_name):
    """Generate a Sales Invoice from a Load document."""
    load = frappe.get_doc("Load", load_name)

    if load.invoice_generated:
        frappe.throw(_("Invoice already generated for this load."))

    if not load.customer:
        frappe.throw(_("Load must have a Customer linked."))

    # Gather quantities per service item
    service_qty = {}
    hdd_count = 0
    tape_count = 0
    total_items = 0

    for item in load.material_items or []:
        mt = item.material_type or ""
        qty = item.total_count or 0
        if qty <= 0:
            continue
        total_items += qty
        if mt in HDD_MATERIALS:
            hdd_count += qty
        elif mt in TAPE_MATERIALS:
            tape_count += qty

    # Determine if on-site
    is_onsite = (load.service_type or "") == "On-site"
    destruction = load.destruction_method or "Wipe"

    # For simplicity, assume "has hardware" = True unless explicitly set otherwise.
    # TODO: Add custom field `has_hardware` on Load if needed.
    has_hardware = True

    invoice_items = []

    if is_onsite and hdd_count > 0:
        onsite_item = _get_onsite_item(has_hardware)
        base_rate = _calculate_tier_price(onsite_item, hdd_count)
        # Onsite: base fee + per-drive for extras above 100
        if hdd_count <= 100:
            invoice_items.append({
                "item_code": onsite_item,
                "qty": 1,
                "rate": base_rate,
                "description": f"On-site shredding for {hdd_count} drives"
            })
        else:
            # One base fee + per-drive extras
            invoice_items.append({
                "item_code": onsite_item,
                "qty": 1,
                "rate": base_rate,
                "description": f"On-site base fee (1-100 drives)"
            })
            extra = hdd_count - 100
            per_drive_rate = _calculate_tier_price(onsite_item, hdd_count)
            invoice_items.append({
                "item_code": onsite_item,
                "qty": extra,
                "rate": per_drive_rate,
                "description": f"Additional on-site drives ({extra})"
            })
    else:
        # Standard pickup/mail-in pricing
        if hdd_count > 0:
            hdd_item = _get_service_item(destruction, has_hardware)
            if hdd_item:
                rate = _calculate_tier_price(hdd_item, hdd_count)
                invoice_items.append({
                    "item_code": hdd_item,
                    "qty": hdd_count,
                    "rate": rate,
                    "description": f"{hdd_item} for {hdd_count} drives"
                })

    if tape_count > 0:
        tape_item = _get_tape_item(destruction, has_hardware)
        rate = _calculate_tier_price(tape_item, tape_count)
        invoice_items.append({
            "item_code": tape_item,
            "qty": tape_count,
            "rate": rate,
            "description": f"{tape_item} for {tape_count} tapes"
        })

    # Video recording surcharge
    if load.video_recording and total_items > 0:
        vid_rate = _calculate_tier_price("SVR-VIDEO-RECORD", total_items)
        invoice_items.append({
            "item_code": "SVR-VIDEO-RECORD",
            "qty": total_items,
            "rate": vid_rate,
            "description": f"Video recording surcharge for {total_items} items"
        })

    if not invoice_items:
        frappe.throw(_("No billable items found in this load."))

    # Create Sales Invoice
    si = frappe.new_doc("Sales Invoice")
    si.customer = load.customer
    si.posting_date = frappe.utils.today()
    si.due_date = frappe.utils.today()
    si.price_list = "2025 Service Rates"
    si.selling_price_list = "2025 Service Rates"
    si.currency = "USD"
    si.set_warehouse = None
    si.update_stock = 0

    for it in invoice_items:
        si.append("items", {
            "item_code": it["item_code"],
            "qty": it["qty"],
            "rate": it["rate"],
            "description": it.get("description", ""),
            "uom": "Unit"
        })

    si.save()
    # Do NOT submit automatically; let user review

    # Update Load
    load.invoice_generated = 1
    load.sales_invoice = si.name
    load.save()

    frappe.db.commit()
    return {"status": "ok", "sales_invoice": si.name}
