import frappe, json
from frappe.utils.pdf import get_pdf
from custom_ui.api.db.general import get_doc_history
from custom_ui.db_utils import DbUtils, process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
from custom_ui.api.db.clients import check_if_customer, convert_lead_to_customer
from custom_ui.services import ItemService, DbService, ClientService, AddressService, ContactService, EstimateService, ItemService
from frappe.email.doctype.email_template.email_template import get_email_template

# ===============================================================================
# ESTIMATES & INVOICES API METHODS
# ===============================================================================

@frappe.whitelist()
def get_estimate_table_data_v2(filters={}, sortings=[], page=1, page_size=10):
    """Get paginated estimate table data with filtering and sorting."""
    print("DEBUG: Raw estimate options received:", filters, sortings, page, page_size)
    filters, sortings, page, page_size = DbUtils.process_datatable_request(filters, sortings, page, page_size)
    sortings = "modified desc" if not sortings else sortings
    count = frappe.db.count("Quotation", filters=filters)
    print(f"DEBUG: Number of estimates returned: {count}")
    estimate_names = frappe.db.get_all(
        "Quotation",
        filters=filters,
        pluck="name",
        limit=page_size,
        start=(page) * page_size,
        order_by=sortings
    )
    
    estimates = [frappe.get_doc("Quotation", name).as_dict() for name in estimate_names]
    tableRows = []
    for estimate in estimates:
        tableRow = {
            "id": estimate["name"],
            "address": frappe.db.get_value("Address", estimate.get("custom_job_address"), "full_address"),
            # strip "-#-" from actual_customer_name and anything that comes after it
            "customer": estimate.get("actual_customer_name").split("-#-")[0] if estimate.get("actual_customer_name") else estimate.get("customer_name") if estimate.get("customer_name") else "",
            "status": estimate.get("custom_current_status", ""),
            "order_type": estimate.get("order_type", ""),
        }
        tableRows.append(tableRow)
    table_data_dict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size)
    return build_success_response(table_data_dict)
    

@frappe.whitelist()
def get_estimate_table_data(filters={}, sortings=[], page=1, page_size=10):
    """Get paginated estimate table data with filtering and sorting support."""
    print("DEBUG: Raw estimate options received:", filters, sortings, page, page_size)

    processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size)

    if is_or:
        count = frappe.db.sql(*get_count_or_filters("Quotation", processed_filters))[0][0]
    else:
        count = frappe.db.count("Quotation", filters=processed_filters)

    print(f"DEBUG: Number of estimates returned: {count}")

    estimates = frappe.db.get_all(
        "Quotation",
        fields=["*"],
        filters=processed_filters if not is_or else None,
        or_filters=processed_filters if is_or else None,
        limit=page_size,
        start=page * page_size,
        order_by=processed_sortings
    )

    tableRows = []
    for estimate in estimates:
        full_address = frappe.db.get_value("Address", estimate.get("custom_job_address"), "full_address")
        tableRow = {}
        tableRow["id"] = estimate["name"]
        tableRow["address"] = full_address
        tableRow["quotation_to"] = estimate.get("quotation_to", "")
        tableRow["customer"] = estimate.get("party_name", "")
        tableRow["status"] = estimate.get("custom_current_status", "")
        tableRow["date"] = estimate.get("transaction_date", "")
        tableRow["order_type"] = estimate.get("order_type", "")
        tableRow["items"] = estimate.get("items", "")
        tableRows.append(tableRow)

    table_data_dict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size)
    return build_success_response(table_data_dict)


@frappe.whitelist()
def get_quotation_items(project_template:str = None):
    """Get all available quotation items."""
    try:
        filters = EstimateService.map_project_template_to_filter(project_template)
        items = frappe.get_all("Item", fields=["item_code", "item_group"], filters=filters)
        grouped_item_dicts = {}
        for item in items:
            item_dict = ItemService.get_full_dict(item.item_code)
            if item_dict["bom"]:
                if "Packages" not in grouped_item_dicts:
                    grouped_item_dicts["Packages"] = {}
                if item.item_group not in grouped_item_dicts["Packages"]:
                    grouped_item_dicts["Packages"][item.item_group] = []
                grouped_item_dicts["Packages"][item.item_group].append(item_dict)
            else:
                if item.item_group not in grouped_item_dicts:
                    grouped_item_dicts[item.item_group] = []
                grouped_item_dicts[item.item_group].append(item_dict)
        return build_success_response(grouped_item_dicts)
    except Exception as e:
        return build_error_response(str(e), 500)

@frappe.whitelist()
def get_estimate(estimate_name):
    """Get detailed information for a specific estimate."""
    try:
        estimate = frappe.get_doc("Quotation", estimate_name)
        est_dict = estimate.as_dict()

        address_name = estimate.custom_job_address or estimate.customer_address
        if address_name:
            # Fetch Address Doc
            address_doc = frappe.get_doc("Address", address_name).as_dict()
            est_dict["full_address"] = address_doc.get("full_address")

            # Logic from get_address_by_full_address to populate customer and contacts
            customer_exists = frappe.db.exists("Customer", address_doc.get("custom_customer_to_bill"))
            doctype = "Customer" if customer_exists else "Lead"
            name = ""
            if doctype == "Customer":
                name = address_doc.get("custom_customer_to_bill")
            else:
                lead_links = address_doc.get("links", [])
                lead_name = [link.link_name for link in lead_links if link.link_doctype == "Lead"]
                name = lead_name[0] if lead_name else ""

            if name:
                address_doc["customer"] = frappe.get_doc(doctype, name).as_dict()

            contacts = []
            if address_doc.get("custom_linked_contacts"):
                for contact_link in address_doc.get("custom_linked_contacts"):
                    contact_doc = frappe.get_doc("Contact", contact_link.contact)
                    contacts.append(contact_doc.as_dict())
            address_doc["contacts"] = contacts

            est_dict["address_details"] = address_doc

            est_dict["history"] = get_doc_history("Quotation", estimate_name)
            est_dict["items"] = [ItemService.get_full_dict(item.item_code) for item in estimate.items]

        return build_success_response(est_dict)
    except Exception as e:
        return build_error_response(str(e), 500)

@frappe.whitelist()
def get_estimate_items():
    items = frappe.db.get_all("Quotation Item", fields=["*"])
    return build_success_response(items)

@frappe.whitelist()
def get_estimate_from_address(full_address):
    address_name = frappe.db.get_value("Address", {"full_address": full_address}, "name")
    quotation_name = frappe.db.get_value("Quotation", {"custom_job_address": address_name}, "name")
    quotation_doc = frappe.get_doc("Quotation", quotation_name)
    return build_success_response(quotation_doc.as_dict())
    # quotation = frappe.db.sql("""
    #     SELECT q.name, q.custom_job_address
    #     FROM `tabQuotation` q
    #     JOIN `tabAddress` a
    #       ON q.custom_job_address = a.name
    #     WHERE a.full_address =%s
    #     """, (full_address,), as_dict=True)
    # if quotation:
    #     return build_success_response(quotation)
    # else:
    #     return build_error_response("No quotation found for the given address.", 404)

# @frappe.whitelist()
# def send_estimate_email(estimate_name):
#     print("DEBUG: Queuing email send job for estimate:", estimate_name)
#     frappe.enqueue(
#         "custom_ui.api.db.estimates.send_estimate_email_job",
#         estimate_name=estimate_name,
#         queue="long",  # or "default"
#         timeout=600,
#     )
#     return build_success_response("Email queued for sending.")

@frappe.whitelist()
def send_estimate_email(estimate_name):
# def send_estimate_email_job(estimate_name):
    try:
        print("DEBUG: Sending estimate email for:", estimate_name)
        quotation = frappe.get_doc("Quotation", estimate_name)

        # Get recipient email
        if not DbService.exists("Contact", quotation.contact_person):
            return build_error_response("No email found for the customer.", 400)
        party = ContactService.get_or_throw(quotation.contact_person)

        email = quotation.contact_email or None
        if not email:
            if (getattr(party, 'email_id', None)):
                email = party.email_id
            elif (getattr(party, 'email_ids', None) and len(party.email_ids) > 0):
                primary = next((e for e in party.email_ids if e.is_primary), None)
                email = primary.email_id if primary else party.email_ids[0].email_id

        if not email and quotation.custom_job_address:
            address = frappe.get_doc("Address", quotation.custom_job_address)
            email = getattr(address, 'email_id', None)
        if not email:
            return build_error_response("No email found for the customer or address.", 400)

        # Get customer name
        customer_name = party.first_name or party.name or "Valued Customer"
        if party.last_name:
            customer_name = f"{party.first_name} {party.last_name}"

        # Get full address
        full_address = "Address not specified"
        if quotation.custom_job_address:
            address_doc = frappe.get_doc("Address", quotation.custom_job_address)
            full_address = address_doc.full_address or address_doc.address_line1 or "Address not specified"

        # Format price
        price = frappe.utils.fmt_money(quotation.grand_total, currency=quotation.currency)

        # Get additional notes
        additional = quotation.terms or ""

        # Get company phone
        company_phone = ""
        if quotation.company:
            company_doc = frappe.get_doc("Company", quotation.company)
            company_phone = getattr(company_doc, 'phone_no', '') or getattr(company_doc, 'phone', '')

        # Get base URL
        base_url = frappe.utils.get_url()

        # Get letterhead image
        letterhead_image = ""
        if quotation.letter_head:
            letterhead_doc = frappe.get_doc("Letter Head", quotation.letter_head)
            if letterhead_doc.image:
                letterhead_image = frappe.utils.get_url() + letterhead_doc.image

        # Prepare template context
        template_context = {
            "company": quotation.company,
            "customer_name": customer_name,
            "price": price,
            "address": full_address,
            "additional": additional,
            "company_phone": company_phone,
            "base_url": base_url,
            "estimate_name": quotation.name,
            "letterhead_image": letterhead_image
        }

        # Render the email template
        template_path = "custom_ui/templates/emails/general_estimation.html"
        message = frappe.render_template(template_path, template_context)
        subject = f"Estimate from {quotation.company} - {quotation.name}"

        print("DEBUG: Subject:", subject)
        print("DEBUG: Sending email to:", email)

        # Generate PDF attachment
        html = frappe.get_print("Quotation", quotation.name, print_format="Quotation - SNW - Standard", letterhead=True)
        print("DEBUG: Generated HTML for PDF.")
        pdf = get_pdf(html)
        print("DEBUG: Generated PDF for email attachment.")

        # Send email
        frappe.sendmail(
            recipients=email,
            subject=subject,
            message=message,
            doctype="Quotation",
            name=quotation.name,
            read_receipt=1,
            print_letterhead=1,
            attachments=[{"fname": f"{quotation.name}.pdf", "fcontent": pdf}]
        )
        print(f"DEBUG: Email sent to {email} successfully.")
        
        # Update quotation status
        quotation.custom_current_status = "Submitted"
        quotation.custom_sent = 1
        quotation.save()
        quotation.submit()
        frappe.db.commit()
        
        updated_quotation = frappe.get_doc("Quotation", estimate_name)
        return build_success_response(updated_quotation.as_dict())
    except Exception as e:
        print(f"DEBUG: Error in send_estimate_email: {str(e)}")
        return build_error_response(str(e), 500)


@frappe.whitelist()
def manual_response(name, response):
    """Update the response for an estimate in the UI."""
    print("DEBUG: RESPONSE_RECEIVED:", name, response)
    try:
        if not frappe.db.exists("Quotation", name):
            raise Exception("Estimate not found.")
        estimate = frappe.get_doc("Quotation", name)
        if estimate.docstatus != 1:
            raise Exception("Estimate must be submitted to update response.")
        accepted = True if response == "Accepted" else False
        new_status = "Estimate Accepted" if accepted else "Lost"

        estimate.custom_response = response
        estimate.custom_current_status = new_status
        # estimate.custom_current_status = new_status
        # estimate.status = "Ordered" if accepted else "Closed"
        estimate.flags.ignore_permissions = True
        print("DEBUG: Updating estimate with response:", response, "and status:", new_status)
        estimate.save()
        return build_success_response(estimate.as_dict())
    except Exception as e:
        return build_error_response(str(e), 500)


@frappe.whitelist()
def get_estimate_templates(company):
    """Get available estimate templates."""
    filters = {"is_active": 1}
    if company:
        filters["company"] = company
    try:
        print("DEBUG: Fetching estimate templates for company:", company)
        templates = frappe.get_all("Quotation Template", fields=["*"], filters=filters)

        result = []
        if not templates:
            print("DEBUG: No templates found.")
            return build_success_response(result)
        print(f"DEBUG: Found {len(templates)} templates.")
        for template in templates:
            print("DEBUG: Processing template:", template)
            items = frappe.get_all("Quotation Template Item",
                                 fields=["item_code", "item_name", "description", "quantity", "discount_percentage", "rate"],
                                 filters={"parent": template.name},
                                 order_by="idx")

            # Map fields to camelCase as requested
            mapped_items = []
            for item in items:
                mapped_items.append({
                    "item_code": item.item_code,
                    "item_name": item.item_name,
                    "description": item.description,
                    "quantity": item.quantity,
                    "discount_percentage": item.discount_percentage,
                    "rate": item.rate
                })

            result.append({
                "name": template.name,
                "template_name": template.template_name,
                "active": template.is_active,
                "description": template.description,
                "items": mapped_items,
                "project_template": template.project_template,
            })

        return build_success_response(result)
    except Exception as e:
        return build_error_response(str(e), 500)

@frappe.whitelist()
def create_estimate_template(data):
    """Create a new estimate template."""
    try:
        print("DEBUG: Creating estimate template with data:", data)
        data = json.loads(data) if isinstance(data, str) else data

        doc_data = {
            "doctype": "Quotation Template",
            "is_active": 1,
            "description": data.get("description"),
            "company": data.get("company"),
            "items": [],
            "template_name": data.get("template_name"),
            "custom_project_template": data.get("project_template", ""),
            "source_quotation": data.get("source_quotation", "")
        }


        new_template = frappe.get_doc(doc_data)

        for item in data.get("items", []):
            new_template.append("items", {
                "item_code": item.get("item_code"),
                "item_name": item.get("item_name"),
                "description": item.get("description"),
                "qty": item.get("qty") or item.get("quantity"),
                "rate": item.get("standard_rate") or item.get("rate"),
                "discount_percentage": item.get("discount_percentage")
            })

        new_template.insert()
        return build_success_response(new_template.name)
    except Exception as e:
        return build_error_response(str(e), 500)

# @frappe.whitelist()
# def create_template(data):
#     """Create a new estimate template."""
#     try:
#         data = json.loads(data) if isinstance(data, str) else data
#         print("DEBUG: Creating estimate template with data:", data)

#         new_template = frappe.get_doc({
#             "doctype": "Quotation Template",
#             "template_name": data.get("templateName"),
#             "is_active": data.get("active", 1),
#             "description": data.get("description", ""),
#             "company": data.get("company", ""),
#             "source_quotation": data.get("source_quotation", "")
#         })

#         for item in data.get("items", []):
#             item = json.loads(item) if isinstance(item, str) else item
#             new_template.append("items", {
#                 "item_code": item.get("itemCode"),
#                 "item_name": item.get("itemName"),
#                 "description": item.get("description"),
#                 "qty": item.get("quantity"),
#                 "discount_percentage": item.get("discountPercentage"),
#                 "rate": item.get("rate")
#             })

#         new_template.insert()
#         print("DEBUG: New estimate template created with name:", new_template.name)
#         return build_success_response(new_template.as_dict())
#     except Exception as e:
#         return build_error_response(str(e), 500)

@frappe.whitelist()
def upsert_estimate(data):
    """Create or update an estimate."""
    try:
        data = json.loads(data) if isinstance(data, str) else data
        print("DEBUG: Upsert estimate data:", data)
        address_doc = AddressService.get_or_throw(data.get("address_name"))
        estimate_name = data.get("estimate_name")
        project_template = data.get("project_template", None)

        # If estimate_name exists, update existing estimate
        if estimate_name:
            print(f"DEBUG: Updating existing estimate: {estimate_name}")
            estimate = frappe.get_doc("Quotation", estimate_name)

            # Update fields
            # estimate.custom_installation_address = data.get("address")
            # estimate.custom_job_address = data.get("address_name")
            # estimate.party_name = data.get("customer")
            # estimate.contact_person = data.get("contact_name")
            estimate.requires_half_payment = data.get("requires_half_payment", 0)
            estimate.custom_project_template = project_template
            estimate.custom_quotation_template = data.get("quotation_template", None)
            # estimate.company = data.get("company")
            # estimate.contact_email = data.get("contact_email")
            # estimate.quotation_to = client_doctype
            # estimate.customer_name = data.get("customer")
            # estimate.customer_address = data.get("address_name")
            # estimate.letter_head = data.get("company")
            # estimate.from_onsite_meeting = data.get("onsite_meeting", None)

            # Clear existing items and add new ones
            estimate.items = []
            for item in data.get("items", []):
                item = json.loads(item) if isinstance(item, str) else item
                estimate.append("items", {
                    "item_code": item.get("item_code"),
                    "qty": item.get("qty"),
                    "rate": item.get("rate"),
                    "discount_amount": item.get("discount_amount") or item.get("discountAmount", 0),
                    "discount_percentage": item.get("discount_percentage") or item.get("discountPercentage", 0)
                })

            estimate.save()
            frappe.db.commit()
            estimate_dict = estimate.as_dict()
            estimate_dict["history"] = get_doc_history("Quotation", estimate_name)
            print(f"DEBUG: Estimate updated: {estimate.name}")
            return build_success_response(estimate_dict)

        # Otherwise, create new estimate
        else:
            print("DEBUG: Creating new estimate")
            print("DEBUG: Retrieved address name:", data.get("address_name"))
            client_doc = ClientService.get_client_or_throw(address_doc.customer_name)
            # billing_address = next((addr for addr in address_doc if addr.address_type == "Billing"), None)
            # if billing_address:
            #     print("DEBUG: Found billing address:", billing_address.name)
            # else:
            #     print("DEBUG: No billing address found for client:", client_doc.name)
            new_estimate = frappe.get_doc({
                "doctype": "Quotation",
                "requires_half_payment": data.get("requires_half_payment", 0),
                "custom_job_address": data.get("address_name"),
                "custom_current_status": "Draft",
                "contact_email": data.get("contact_email"),
                "party_name": data.get("contact_name"),
                "quotation_to": "Contact",
                "company": data.get("company"),
                "actual_customer_name": client_doc.name,
                "customer_type": address_doc.customer_type,
                "customer_address": client_doc.custom_billing_address,
                "contact_person": data.get("contact_name"),
                "letter_head": data.get("company"),
                "custom_project_template": data.get("project_template", None),
                "custom_quotation_template": data.get("quotation_template", None),
                "from_onsite_meeting": data.get("from_onsite_meeting", None)
            })
            for item in data.get("items", []):
                item = json.loads(item) if isinstance(item, str) else item
                new_estimate.append("items", {
                    "item_code": item.get("item_code"),
                    "qty": item.get("qty"),
                    "rate": item.get("rate"),
                    "discount_amount": item.get("discount_amount") or item.get("discountAmount", 0),
                    "discount_percentage": item.get("discount_percentage") or item.get("discountPercentage", 0)
                })
                # Iterate through every field and print it out, I need to see if there is any field that is a Dynamic link saying Customer
                for fieldname, value in new_estimate.as_dict().items():
                    print(f"DEBUG: Field '{fieldname}': {value}")
            new_estimate.insert()
            # AddressService.append_link(data.get("address_name"), "quotations", "quotation", new_estimate.name)
            # ClientService.append_link(data.get("customer"), "quotations", "quotation", new_estimate.name)
            print("DEBUG: New estimate created with name:", new_estimate.name)
            dict = new_estimate.as_dict()
            dict["items"] = [ItemService.get_full_dict(item.item_code) for item in new_estimate.items]
            return build_success_response(dict)
    except Exception as e:
        print(f"DEBUG: Error in upsert_estimate: {str(e)}")
        return build_error_response(str(e), 500)


@frappe.whitelist()
def get_unapproved_estimates_count(company):
    """Get the number of unapproved estimates."""
    try:
        draft_filters = {'status': "Draft", "company": company}
        submitted_filters = {'status': "Submitted", "company": company}
        draft_count = frappe.db.count("Quotation", filters=draft_filters)
        submitted_count = frappe.db.count("Quotation", filters=submitted_filters)
        return build_success_response([draft_count, submitted_count])
    except Exception as e:
        return build_error_response(str(e), 500)


@frappe.whitelist()
def get_estimates_half_down_count(company):
    """Get the number unpaid half-down estimates."""
    try:
        filters = {'requires_half_payment': True, 'company': company}
        count = frappe.db.count("Quotation", filters=filters)
        return build_success_response([count])
    except Exception as e:
        return build_error_response(str(e), 500)


def get_estimate_history(estimate_name):
    """Get the history of changes for a specific estimate."""
    pass
    # return history

# @frappe.whitelist()
# def get_estimate_counts():
#     """Get specific counts of estimates based on their status."""
#     try:
#         counts = {
#             "total_estimates": frappe.db.count("Quotation"),
#             "ready_to_"
#         }

