import frappe
from frappe.model.document import Document
from .db_service import DbService
from erpnext.crm.doctype.lead.lead import make_customer
from .address_service import AddressService
from .contact_service import ContactService
from .estimate_service import EstimateService
from .onsite_meeting_service import OnSiteMeetingService

class ClientService:
    
    @staticmethod
    def get_client_or_throw(client_name: str) -> Document:
        """Retrieve a Client document (Customer or Lead) or throw an error if it does not exist."""
        doctype = ClientService.get_client_doctype(client_name)
        return DbService.get_or_throw(doctype, client_name)
    
    @staticmethod
    def get_client_doctype(client_name: str) -> str:
        """Determine if the client is a Customer or Lead."""
        if DbService.exists("Customer", client_name):
            return "Customer"
        elif DbService.exists("Lead", client_name):
            return "Lead"
        else:
            raise ValueError(f"Client with name {client_name} does not exist as Customer or Lead.")
    
    @staticmethod
    def set_primary_contact(client_name: str, contact_name: str):
        """Set the primary contact for a client (Customer or Lead)."""
        print(f"DEBUG: Setting primary contact for client {client_name} to contact {contact_name}")
        client_doctype = ClientService.get_client_doctype(client_name)
        frappe.db.set_value(client_doctype, client_name, "primary_contact", contact_name)
        print(f"DEBUG: Set primary contact for client {client_name} to contact {contact_name}")
        
    @staticmethod
    def append_link(client_name: str, field: str, link_doctype: str, link_name: str):
        """Set a link field for a client (Customer or Lead)."""
        print(f"DEBUG: Setting link field {field} for client {client_name} to {link_doctype} {link_name}")
        client_doctype = ClientService.get_client_doctype(client_name)
        client_doc = frappe.get_doc(client_doctype, client_name)
        client_doc.append(field, {
            link_doctype.lower(): link_name
        })
        client_doc.save(ignore_permissions=True)
        print(f"DEBUG: Set link field {field} for client {client_doc.get('name')} to {link_doctype} {link_name}")
    
    @staticmethod
    def append_link_v2(client_name: str, field: str, link: dict):
        """Set a link field for a client (Customer or Lead) using a link dictionary."""
        print(f"DEBUG: Setting link field {field} for client {client_name} with link data {link}")
        client_doctype = ClientService.get_client_doctype(client_name)
        client_doc = DbService.get_or_throw(client_doctype, client_name)
        print("DEBUG: Appending link:", link)
        client_doc.append(field, link)
        print("DEBUG: Saving client document after appending link.")
        client_doc.save(ignore_permissions=True)
        frappe.db.commit()
        print(f"DEBUG: Set link field {field} for client {client_doc.get('name')} with link data {link}")
    
    @staticmethod
    def convert_lead_to_customer(
        lead_name: str, 
        update_quotations: bool = True, 
        update_addresses: bool = True, 
        update_contacts: bool = True,
        update_onsite_meetings: bool = True,
        update_companies: bool = True
        ) -> Document:
        """Convert a Lead to a Customer."""
        print(f"DEBUG: Converting Lead {lead_name} to Customer")
        try:
            lead_doc = DbService.get_or_throw("Lead", lead_name)
            print(f"DEBUG: Retrieved Lead document: {lead_doc.name}")
            
            print("DEBUG: RUNNING make_customer()")
            customer_doc = make_customer(lead_doc.name)
            print(f"DEBUG: make_customer() returned document type: {type(customer_doc)}")
            print(f"DEBUG: Customer doc name: {customer_doc.name if hasattr(customer_doc, 'name') else 'NO NAME'}")
            customer_doc.custom_billing_address = lead_doc.custom_billing_address
            print("DEBUG: Calling customer_doc.insert()")
            customer_doc.insert(ignore_permissions=True)
            print(f"DEBUG: Customer inserted successfully: {customer_doc.name}")
            
            frappe.db.commit()
            print("DEBUG: Database committed after customer insert")
            print("DEBUG: CREATED CUSTOMER:", customer_doc.as_dict())
            if update_addresses:
                print("DEBUG: Lead_doc addresses:", lead_doc.get("addresses", []))
                print(f"DEBUG: Updating addresses. Count: {len(lead_doc.get('properties', []))}")
                for address in lead_doc.get("properties", []):
                    try:
                        print(f"DEBUG: Processing address: {address.get('address')}")
                        ClientService.append_link_v2(customer_doc.name, "properties", {"address": address.get("address")})
                        customer_doc.reload()
                        address_doc = AddressService.get_or_throw(address.get("address"))
                        AddressService.link_address_to_customer(address_doc, "Customer", customer_doc.name)
                        print(f"DEBUG: Linked address {address.get('address')} to customer")
                    except Exception as e:
                        print(f"ERROR: Failed to link address {address.get('address')}: {str(e)}")
                        frappe.log_error(f"Address linking error: {str(e)}", "convert_lead_to_customer")
                        
            if update_contacts:
                print(f"DEBUG: Updating contacts. Count: {len(lead_doc.get('contacts', []))}")
                for contact in lead_doc.get("contacts", []):
                    try:
                        print(f"DEBUG: Processing contact: {contact.get('contact')}")
                        ClientService.append_link_v2(customer_doc.name, "contacts", {"contact": contact.get("contact")})
                        customer_doc.reload()
                        contact_doc = ContactService.get_or_throw(contact.get("contact"))
                        ContactService.link_contact_to_customer(contact_doc, "Customer", customer_doc.name)
                        print(f"DEBUG: Linked contact {contact.get('contact')} to customer")
                    except Exception as e:
                        print(f"ERROR: Failed to link contact {contact.get('contact')}: {str(e)}")
                        frappe.log_error(f"Contact linking error: {str(e)}", "convert_lead_to_customer")
                        
            if update_quotations:
                print(f"DEBUG: Updating quotations. Count: {len(lead_doc.get('quotations', []))}")
                for quotation in lead_doc.get("quotations", []):
                    try:
                        print(f"DEBUG: Processing quotation: {quotation.get('quotation')}")
                        ClientService.append_link_v2(customer_doc.name, "quotations", {"quotation": quotation.get("quotation")})
                        customer_doc.reload()
                        quotation_doc = EstimateService.get_or_throw(quotation.get("quotation"))
                        EstimateService.link_estimate_to_customer(quotation_doc, "Customer", customer_doc.name)
                        print(f"DEBUG: Linked quotation {quotation.get('quotation')} to customer")
                    except Exception as e:
                        print(f"ERROR: Failed to link quotation {quotation.get('quotation')}: {str(e)}")
                        
            if update_onsite_meetings:
                print(f"DEBUG: Updating onsite meetings. Count: {len(lead_doc.get('onsite_meetings', []))}")
                for meeting in lead_doc.get("onsite_meetings", []):
                    try:
                        print(f"DEBUG: Processing onsite meeting: {meeting.get('onsite_meeting')}")
                        meeting_doc = DbService.get_or_throw("On-Site Meeting",meeting.get("onsite_meeting"))
                        ClientService.append_link_v2(customer_doc.name, "onsite_meetings", {"onsite_meeting": meeting.get("onsite_meeting")})
                        customer_doc.reload()
                        OnSiteMeetingService.link_onsite_meeting_to_customer(meeting_doc, "Customer", customer_doc.name)
                        print(f"DEBUG: Linked onsite meeting {meeting.get('onsite_meeting')} to customer")
                    except Exception as e:
                        print(f"ERROR: Failed to link onsite meeting {meeting.get('onsite_meeting')}: {str(e)}")
                        frappe.log_error(f"Onsite meeting linking error: {str(e)}", "convert_lead_to_customer")
            if update_companies:
                print(f"DEBUG: Updating companies. Count: {len(lead_doc.get('companies', []))}")
                for company in lead_doc.get("companies", []):
                    try:
                        print(f"DEBUG: Processing company: {company.get('company')}")
                        ClientService.append_link_v2(customer_doc.name, "companies", {"company": company.get("company")})
                        customer_doc.reload()
                        print(f"DEBUG: Linked company {company.get('company')} to customer")
                    except Exception as e:
                        print(f"ERROR: Failed to link company {company.get('company')}: {str(e)}")
                        frappe.log_error(f"Company linking error: {str(e)}", "convert_lead_to_customer")
            print(f"DEBUG: Converted Lead {lead_name} to Customer {customer_doc.name}")
            frappe.db.commit()
            return customer_doc
            
        except Exception as e:
            print(f"ERROR: Exception in convert_lead_to_customer: {str(e)}")
            frappe.log_error(f"convert_lead_to_customer failed: {str(e)}", "ClientService")
            raise