import ApiUtils from "./apiUtils";
import DataUtils from "./utils";
import { useErrorStore } from "./stores/errors";

const ZIPPOPOTAMUS_BASE_URL = "https://api.zippopotam.us/us";
// Proxy method for external API calls
const FRAPPE_PROXY_METHOD = "custom_ui.api.proxy.request";
// On-Site Meeting methods
const FRAPPE_GET_INCOMPLETE_BIDS_METHOD = "custom_ui.api.db.on_site_meetings.get_incomplete_bids";
// Estimate methods
const FRAPPE_UPSERT_ESTIMATE_METHOD = "custom_ui.api.db.estimates.upsert_estimate";
const FRAPPE_GET_ESTIMATES_METHOD = "custom_ui.api.db.estimates.get_estimate_table_data";
const FRAPPE_GET_ESTIMATES_TABLE_DATA_V2_METHOD = "custom_ui.api.db.estimates.get_estimate_table_data_v2";
const FRAPPE_GET_ESTIMATE_BY_ADDRESS_METHOD = "custom_ui.api.db.estimates.get_estimate_from_address";
const FRAPPE_SEND_ESTIMATE_EMAIL_METHOD = "custom_ui.api.db.estimates.send_estimate_email";
const FRAPPE_LOCK_ESTIMATE_METHOD = "custom_ui.api.db.estimates.lock_estimate";
const FRAPPE_ESTIMATE_UPDATE_RESPONSE_METHOD = "custom_ui.api.db.estimates.manual_response";
const FRAPPE_GET_ESTIMATE_TEMPLATES_METHOD = "custom_ui.api.db.estimates.get_estimate_templates";
const FRAPPE_CREATE_ESTIMATE_TEMPLATE_METHOD = "custom_ui.api.db.estimates.create_estimate_template";
const FRAPPE_GET_UNAPPROVED_ESTIMATES_COUNT_METHOD = "custom_ui.api.db.estimates.get_unapproved_estimates_count";
const FRAPPE_GET_ESTIMATES_HALF_DOWN_COUNT_METHOD = "custom_ui.api.db.estimates.get_estimates_half_down_count";
// Item methods
const FRAPPE_SAVE_AS_PACKAGE_ITEM_METHOD = "custom_ui.api.db.items.save_as_package_item";
const FRAPPE_GET_ITEMS_BY_PROJECT_TEMPLATE_METHOD = "custom_ui.api.db.items.get_by_project_template";
// Job methods
const FRAPPE_GET_JOB_METHOD = "custom_ui.api.db.jobs.get_job";
const FRAPPE_GET_JOBS_METHOD = "custom_ui.api.db.jobs.get_jobs_table_data";
const FRAPPE_UPSERT_JOB_METHOD = "custom_ui.api.db.jobs.upsert_job";
const FRAPPE_GET_JOB_TASK_TABLE_DATA_METHOD = "custom_ui.api.db.jobs.get_job_task_table_data";
const FRAPPE_GET_JOB_TASK_LIST_METHOD = "custom_ui.api.db.jobs.get_job_task_list";
const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_projects";
const FRAPPE_GET_JOBS_FOR_CALENDAR_METHOD = "custom_ui.api.db.jobs.get_projects_for_calendar";
const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates";
const FRAPPE_UPDATE_JOB_SCHEDULED_DATES_METHOD = "custom_ui.api.db.jobs.update_job_scheduled_dates";
const FRAPPE_GET_JOBS_IN_QUEUE_METHOD = "custom_ui.api.db.jobs.get_jobs_in_queue_count";
const FRAPPE_GET_JOBS_IN_PROGRESS_METHOD = "custom_ui.api.db.jobs.get_jobs_in_progress_count";
const FRAPPE_GET_JOBS_LATE_METHOD = "custom_ui.api.db.jobs.get_jobs_late_count";
const FRAPPE_GET_JOBS_TO_INVOICE_METHOD = "custom_ui.api.db.jobs.get_jobs_to_invoice_count";
// Task methods
const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_tasks_table_data";
const FRAPPE_GET_TASKS_STATUS_OPTIONS = "custom_ui.api.db.tasks.get_task_status_options";
const FRAPPE_SET_TASK_STATUS_METHOD = "custom_ui.api.db.tasks.set_task_status";
const FRAPPE_GET_TASKS_DUE_METHOD = "custom_ui.api.db.tasks.get_tasks_due";
// Invoice methods
const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data";
const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice";
const FRAPPE_GET_INVOICES_LATE_METHOD = "custom_ui.api.db.invoices.get_invoices_late_count";
const FRAPPE_CREATE_INVOICE_FOR_JOB = "custom_ui.api.db.invoices.create_invoice_for_job";
// Warranty methods
const FRAPPE_GET_WARRANTY_CLAIMS_METHOD = "custom_ui.api.db.warranties.get_warranty_claims";
// On-Site Meeting methods
const FRAPPE_GET_WEEK_ONSITE_MEETINGS_METHOD =
	"custom_ui.api.db.bid_meetings.get_week_bid_meetings";
const FRAPPE_GET_BID_MEETING_NOTE_FORM_METHOD = "custom_ui.api.db.bid_meetings.get_bid_meeting_note_form";
const FRAPPE_GET_ONSITE_MEETINGS_METHOD = "custom_ui.api.db.bid_meetings.get_bid_meetings";
const FRAPPE_SUBMIT_BID_MEETING_NOTE_FORM_METHOD = "custom_ui.api.db.bid_meetings.submit_bid_meeting_note_form";
// Address methods
const FRAPPE_GET_ADDRESSES_METHOD = "custom_ui.api.db.addresses.get_addresses";
// Client methods
const FRAPPE_UPSERT_CLIENT_METHOD = "custom_ui.api.db.clients.upsert_client";
const FRAPPE_GET_CLIENT_STATUS_COUNTS_METHOD = "custom_ui.api.db.clients.get_client_status_counts";
const FRAPPE_GET_CLIENT_TABLE_DATA_METHOD = "custom_ui.api.db.clients.get_clients_table_data";
const FRAPPE_GET_CLIENT_TABLE_DATA_V2_METHOD = "custom_ui.api.db.clients.get_clients_table_data_v2";
const FRAPPE_GET_CLIENT_METHOD = "custom_ui.api.db.clients.get_client_v2";
const FRAPPE_GET_CLIENT_NAMES_METHOD = "custom_ui.api.db.clients.get_client_names";
// Employee methods
const FRAPPE_GET_EMPLOYEES_METHOD = "custom_ui.api.db.employees.get_employees";
const FRAPPE_GET_EMPLOYEES_ORGANIZED_METHOD = "custom_ui.api.db.employees.get_employees_organized";
// Other methods
const FRAPPE_GET_WEEK_HOLIDAYS_METHOD = "custom_ui.api.db.general.get_week_holidays";
const FRAPPE_GET_DOC_LIST_METHOD = "custom_ui.api.db.general.get_doc_list";
// Service Appointment methods
const FRAPPE_GET_SERVICE_APPOINTMENT_METHOD = "custom_ui.api.db.service_appointments.get_service_appointment";
const FRAPPE_GET_SERVICE_APPOINTMENTS_METHOD = "custom_ui.api.db.service_appointments.get_service_appointments";
const FRAPPE_UPDATE_SERVICE_APPOINTMENT_SCHEDULED_DATES_METHOD = "custom_ui.api.db.service_appointments.update_service_appointment_scheduled_dates";
const FRAPPE_UPDATE_SERVICE_APPOINTMENT_STATUS_METHOD = "custom_ui.api.db.service_appointments.update_service_appointment_status";
class Api {
	// ============================================================================
	// CORE REQUEST METHOPD
	// ============================================================================

	static async request(frappeMethod, args = {}) {
		const errorStore = useErrorStore();
		args = ApiUtils.toSnakeCaseObject(args);
		const request = { method: frappeMethod, args };
		console.log("DEBUG: API - Request Args: ", request);
		try {
			let response = await frappe.call(request);
			response = ApiUtils.toCamelCaseObject(response);
			response.method = frappeMethod;
			console.log("DEBUG: API - Request Response: ", response);
			if (response.message.status && response.message.status === "error") {
				throw new Error(response.message.message);
			}
			return response.message.data;
		} catch (error) {
			console.error("ERROR: API - Request Error: ", error);
			errorStore.setApiError("Frappe API", error.message || "API request error");
			throw error;
		}
	}

	// ============================================================================
	// CLIENT METHODS
	// ============================================================================

	static async getClientStatusCounts(params = {}) {
		return await this.request(FRAPPE_GET_CLIENT_STATUS_COUNTS_METHOD, params);
	}

	static async getClientDetails(clientName) {
		return await this.request(FRAPPE_GET_CLIENT_METHOD, { clientName });
	}

	static async getClient(clientName) {
		return await this.request(FRAPPE_GET_CLIENT_METHOD, { clientName });
	}

	static async getPaginatedClientDetailsV2(paginationParams = {}, filters = {}, sortings = []) {
		const { page = 0, pageSize = 10 } = paginationParams;
		const result = await this.request(FRAPPE_GET_CLIENT_TABLE_DATA_V2_METHOD, {
			filters,
			sortings,
			page: page + 1,
			pageSize,
		});
		return result;
	}

	/**
	 * Get paginated client data with filtering and sorting
	 * @param {Object} paginationParams - Pagination parameters from store
	 * @param {Object} filters - Filter parameters from store
	 * @param {Object} sorting - Sorting parameters from store (optional)
	 * @returns {Promise<{data: Array, pagination: Object}>}
	 */
	static async getPaginatedClientDetails(paginationParams = {}, filters = {}, sortings = []) {
		const { page = 0, pageSize = 10 } = paginationParams;
		const result = await this.request(FRAPPE_GET_CLIENT_TABLE_DATA_METHOD, {
			filters,
			sortings,
			page: page + 1,
			pageSize,
		});
		return result;
	}

	static async getCustomerNames(type) {
		return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { type });
	}

	static async getInstallProjects(startDate, endDate) {
		return await this.request(FRAPPE_GET_INSTALL_PROJECTS_METHOD, {
			start_date: startDate,
			end_date: endDate,
		});
	}

	static async upsertJob(data) {
		return await this.request(FRAPPE_UPSERT_JOB_METHOD, { data });
	}

	static async getClientNames(clientName) {
		return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { searchTerm: clientName });
	}

	static async searchClientNames(searchTerm) {
		return await this.request("custom_ui.api.db.clients.search_client_names", { searchTerm });
	}

	static async createClient(clientData) {
		const result = await this.request(FRAPPE_UPSERT_CLIENT_METHOD, { data: clientData });
		console.log("DEBUG: API - Created/Updated Client: ", result);
		return result;
	}

	// ============================================================================
	// ON-SITE MEETING METHODS
	// ============================================================================

	static async getBidMeetingNoteForm(projectTemplate) {
		return await this.request(FRAPPE_GET_BID_MEETING_NOTE_FORM_METHOD, { projectTemplate });
	}

	static async submitBidMeetingNoteForm(data) {
		return await this.request(FRAPPE_SUBMIT_BID_MEETING_NOTE_FORM_METHOD, { 
			bidMeeting: data.bidMeeting, 
			projectTemplate: data.projectTemplate,
			formTemplate: data.formTemplate, 
			fields: data.fields});
	}

	static async getUnscheduledBidMeetings(company) {
		return await this.request(
			"custom_ui.api.db.bid_meetings.get_unscheduled_bid_meetings",
			{ company }
		);
	}

	static async getScheduledBidMeetings(fields = ["*"], filters = {}) {
		return await this.request(FRAPPE_GET_ONSITE_MEETINGS_METHOD, { fields, filters });
	}

	static async getWeekBidMeetings(weekStart, weekEnd, company) {
		return await this.request(FRAPPE_GET_WEEK_ONSITE_MEETINGS_METHOD, { weekStart, weekEnd, company });
	}

	static async updateBidMeeting(name, data) {
		return await this.request("custom_ui.api.db.bid_meetings.update_bid_meeting", {
			name,
			data,
		});
	}

	static async getBidMeeting(name) {
		return await this.request("custom_ui.api.db.bid_meetings.get_bid_meeting", {
			name,
		});
	}

	static async createBidMeeting(data) {
		return await this.request("custom_ui.api.db.bid_meetings.create_bid_meeting", {
			data,
		});
	}

	static async getBidMeetingNote(name) {
		return await this.request("custom_ui.api.db.bid_meetings.get_bid_meeting_note", {
			name,
		});
	}

	// ============================================================================
	// ESTIMATE / QUOTATION METHODS
	// ============================================================================

	static async getQuotationItems(projectTemplate) {
		return await this.request("custom_ui.api.db.estimates.get_quotation_items", { projectTemplate });
	}

	static async getEstimateFromAddress(fullAddress) {
		return await this.request(FRAPPE_GET_ESTIMATE_BY_ADDRESS_METHOD, {
			full_address: fullAddress,
		});
	}

	static async getEstimate(estimateName) {
		return await this.request("custom_ui.api.db.estimates.get_estimate", {
			estimate_name: estimateName,
		});
	}

	static async getEstimateItems() {
		return await this.request("custom_ui.api.db.estimates.get_estimate_items");
	}

	static async getPaginatedEstimateDetails(paginationParams = {}, filters = {}, sorting = null) {
		const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams;

		// Use sorting from the dedicated sorting parameter first, then fall back to pagination params
		const actualSortField = sorting?.field || sortField;
		const actualSortOrder = sorting?.order || sortOrder;

		const options = {
			page: page + 1, // Backend expects 1-based pages
			page_size: pageSize,
			filters,
			sorting:
				actualSortField && actualSortOrder
					? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
					: null,
			for_table: true,
		};

		console.log("DEBUG: API - Sending estimate options to backend:", page, pageSize, filters, sorting);

		const result = await this.request(FRAPPE_GET_ESTIMATES_TABLE_DATA_V2_METHOD, { page, pageSize, filters, sorting});
		return result;
	}

	static async getIncompleteBidsCount(currentCompany) {
		const result = await this.request(FRAPPE_GET_INCOMPLETE_BIDS_METHOD, { company: currentCompany });
		return result;
	}

	static async createEstimate(estimateData) {
		const result = await this.request(FRAPPE_UPSERT_ESTIMATE_METHOD, { data: estimateData });
		return result;
	}

	static async sendEstimateEmail(estimateName) {
		return await this.request(FRAPPE_SEND_ESTIMATE_EMAIL_METHOD, { estimateName });
	}

	static async lockEstimate(estimateName) {
		return await this.request(FRAPPE_LOCK_ESTIMATE_METHOD, { estimateName });
	}

	static async updateEstimateResponse(estimateName, response) {
		return await this.request(FRAPPE_ESTIMATE_UPDATE_RESPONSE_METHOD, {name: estimateName, response});
	}

	static async getEstimateTemplates(company) {
		return await this.request(FRAPPE_GET_ESTIMATE_TEMPLATES_METHOD, { company });
	}

	static async createEstimateTemplate(data) {
		return await this.request(FRAPPE_CREATE_ESTIMATE_TEMPLATE_METHOD, { data });
	}

	static async getUnapprovedEstimatesCount(currentCompany) {
		return await this.request(FRAPPE_GET_UNAPPROVED_ESTIMATES_COUNT_METHOD, {company: currentCompany});
	}

	static async getEstimatesHalfDownCount(currentCompany) {
		return await this.request(FRAPPE_GET_ESTIMATES_HALF_DOWN_COUNT_METHOD, {company: currentCompany});
	}

	// ============================================================================
	// JOB / PROJECT METHODS
	// ============================================================================

	static async getJobTemplates(company) {
		return await this.request(FRAPPE_GET_JOB_TEMPLATES_METHOD, { company });
	}

	static async getJobDetails() {
		const projects = await this.getDocsList("Project");
		const data = [];
		for (let prj of projects) {
			let project = await this.getDetailedDoc("Project", prj.name);
			const tableRow = {
				name: project.name,
				customInstallationAddress: project.custom_installation_address,
				customer: project.customer,
				status: project.status,
				percentComplete: project.percent_complete,
			};
			data.push(tableRow);
		}
		return data;
	}

	/**
	 * Get paginated job data with filtering and sorting
	 * @param {Object} paginationParams - Pagination parameters from store
	 * @param {Object} filters - Filter parameters from store
	 * @param {Object} sorting - Sorting parameters from store (optional)
	 * @returns {Promise<{data: Array, pagination: Object}>}
	 */
	static async getPaginatedJobDetails(paginationParams = {}, filters = {}, sorting = null) {
		const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams;

		// Use sorting from the dedicated sorting parameter first, then fall back to pagination params
		const actualSortField = sorting?.field || sortField;
		const actualSortOrder = sorting?.order || sortOrder;

		const options = {
			page: page + 1, // Backend expects 1-based pages
			page_size: pageSize,
			filters,
			sorting:
				actualSortField && actualSortOrder
					? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
					: null,
			for_table: true,
		};

		console.log("DEBUG: API - Sending job options to backend:", page, pageSize, filters, sorting);

		const result = await this.request(FRAPPE_GET_JOBS_METHOD, { page, pageSize, filters, sorting});
		return result;
	}

	static async getJobsForCalendar(startDate, endDate, company = null, projectTemplates = []) {
		return await this.request(FRAPPE_GET_JOBS_FOR_CALENDAR_METHOD, { startDate, endDate, company, projectTemplates });
	}

	static async updateJobScheduledDates(jobName, newStartDate, newEndDate, foremanName) {
		return await this.request(FRAPPE_UPDATE_JOB_SCHEDULED_DATES_METHOD, {
			jobName,
			newStartDate,
			newEndDate,
			foremanName,
		});
	}

	static async getJobsInQueueCount(currentCompany) {
		return await this.request(FRAPPE_GET_JOBS_IN_QUEUE_METHOD, {company: currentCompany});
	}

	static async getJobsInProgressCount(currentCompany) {
		return await this.request(FRAPPE_GET_JOBS_IN_PROGRESS_METHOD, {company: currentCompany});
	}

	static async getJobsLateCount(currentCompany) {
		return await this.request(FRAPPE_GET_JOBS_LATE_METHOD, {company: currentCompany});
	}

	static async getJobsToInvoiceCount(currentCompany) {
		return await this.request(FRAPPE_GET_JOBS_TO_INVOICE_METHOD, {company: currentCompany});
	}

	static async setJobCompleted(jobName) {
		return await this.request(FRAPPE_SET_JOB_COMPLETE_METHOD, {jobName: jobName});
	}

	static async getJob(jobName) {
		if (frappe.db.exists("Project", jobName)) {
			const result = await this.request(FRAPPE_GET_JOB_METHOD, { jobId: jobName })
			console.log(`DEBUG: API - retrieved Job ${jobName}:`, result);
			return result;
		}
	}

	static async createJob(jobData) {
		const payload = DataUtils.toSnakeCaseObject(jobData);
		const result = await this.request(FRAPPE_UPSERT_JOB_METHOD, { data: payload });
		console.log("DEBUG: API - Created Job: ", result);
		return result;
	}

	static async getJobTaskList(jobName) {
		if (frappe.db.exists("Project", jobName)) {
			const result = await this.request(FRAPPE_GET_JOB_TASK_LIST_METHOD, { jobId: jobName })
			console.log(`DEBUG: API - retrieved task list from job ${jobName}:`, result);
			return result;
		}
		else {
			console.log(`DEBUG: API - no record found for task like from job ${jobName}: `, result);
		}
	}

	static async getPaginatedJobTaskDetails(paginationParams = {}, filters = {}, sorting = null) {
		const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams;

		// Use sorting from the dedicated sorting parameter first, then fall back to pagination params
		const actualSortField = sorting?.field || sortField;
		const actualSortOrder = sorting?.order || sortOrder;

		const options = {
			page: page + 1, // Backend expects 1-based pages
			page_size: pageSize,
			filters,
			sorting:
				actualSortField && actualSortOrder
					? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
					: null,
			for_table: true,
		};

		console.log("DEBUG: API - Sending job task options to backend:", options);

		const result = await this.request(FRAPPE_GET_JOB_TASK_TABLE_DATA_METHOD, { filters, sortings: sorting, page:page+1, pageSize });
		return result;
	}

	// ============================================================================
	// SERVICE APPOINTMENT METHODS
	// ============================================================================

	static async getServiceAppointment(serviceAppointmentName) {
		return await this.request(FRAPPE_GET_SERVICE_APPOINTMENT_METHOD, { serviceAppointmentName });
	}

	static async getServiceAppointments(companies = [], filters = {}) {
		return await this.request(FRAPPE_GET_SERVICE_APPOINTMENTS_METHOD, { companies, filters });
	}

	static async updateServiceAppointmentScheduledDates(serviceAppointmentName, startDate, endDate, crewLeadName, startTime = null, endTime = null) {
		return await this.request(FRAPPE_UPDATE_SERVICE_APPOINTMENT_SCHEDULED_DATES_METHOD, {
			serviceAppointmentName,
			startDate,
			endDate,
			crewLeadName,
			startTime,
			endTime
		})
	}

	static async setServiceAppointmentStatus(serviceAppointmentName, newStatus) {
		return await this.request(FRAPPE_UPDATE_SERVICE_APPOINTMENT_STATUS_METHOD, { serviceAppointmentName, newStatus });
	}

	// ============================================================================
	// TASK METHODS
	// ============================================================================

	/**
	 * Get paginated job data with filtering and sorting
	 * @param {Object} paginationParams - Pagination parameters from store
	 * @param {Object} filters - Filter parameters from store
	 * @param {Object} sorting - Sorting parameters from store (optional)
	 * @returns {Promise<{data: Array, pagination: Object}>}
	 */
	static async getPaginatedTaskDetails(paginationParams = {}, filters = {}, sorting = null) {
		const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams;

		// Use sorting from the dedicated sorting parameter first, then fall back to pagination params
		const actualSortField = sorting?.field || sortField;
		const actualSortOrder = sorting?.order || sortOrder;

		//const options = {
		//	page: page + 1, // Backend expects 1-based pages
		//	page_size: pageSize,
		//	filters,
		//	sorting:
		//		actualSortField && actualSortOrder
		//			? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
		//			: null,
		//	for_table: true,
		//};

		const sortings = actualSortField && actualSortOrder
					? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
					: null;
		console.log("DEBUG: API - Sending task options to backend:", page, pageSize, filters, sortings);

		const result = await this.request(FRAPPE_GET_TASKS_METHOD, {
			filters,
			sorting,
			page: page + 1,
			pageSize,
		});
		return result;
	}

	static async getTaskStatusOptions() {
		console.log("DEBUG: API - Loading Task Status options form the backend.");
		const result = await this.request(FRAPPE_GET_TASKS_STATUS_OPTIONS, {});
		return result;
	}

	static async setTaskStatus(taskName, newStatus) {
		return await this.request(FRAPPE_SET_TASK_STATUS_METHOD, { taskName, newStatus });
	}

	static async getTasksDue(subjectFilter, currentCompany) {
		const result = await this.request(FRAPPE_GET_TASKS_DUE_METHOD, {subjectFilter, currentCompany});
		return result;
	}

	static async getTasksCompleted(subjectFilter) {
		const result = await this.request(FRAPPE_GET_TASKS_COMPLETED_METHOD, {subjectFilter});
		return result;
	}

	// ============================================================================
	// INVOICE / PAYMENT METHODS
	// ============================================================================

	static async getPaginatedInvoiceDetails(paginationParams = {}, filters = {}, sorting = null) {
		const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams;

		// Use sorting from the dedicated sorting parameter first, then fall back to pagination params
		const actualSortField = sorting?.field || sortField;
		const actualSortOrder = sorting?.order || sortOrder;

		const options = {
			page: page + 1, // Backend expects 1-based pages
			page_size: pageSize,
			filters,
			sorting:
				actualSortField && actualSortOrder
					? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
					: null,
			for_table: true,
		};

		console.log("DEBUG: API - Sending invoice options to backend:", options);

		const result = await this.request(FRAPPE_GET_INVOICES_METHOD, { options });
		return result;

	}

	static async createInvoice(invoiceData) {
		const payload = DataUtils.toSnakeCaseObject(invoiceData);
		const result = await this.request(FRAPPE_UPSERT_INVOICE_METHOD, { data: payload });
		console.log("DEBUG: API - Created Invoice: ", result);
		return result;
	}

	static async getInvoicesLateCount(currentCompany) {
		const result = await this.request(FRAPPE_GET_INVOICES_LATE_METHOD, { company: currentCompany });
		return result;
	}

	static async createInvoiceForJob(jobName) {
		const result = await this.request(FRAPPE_CREATE_INVOICE_FOR_JOB, { jobName: jobName });
		return result;
	}

	// ============================================================================
	// WARRANTY METHODS
	// ============================================================================

	static async getWarrantyData() {
		const data = await this.request(FRAPPE_GET_WARRANTY_CLAIMS_METHOD);
		console.log("DEBUG: API - getWarrantyData result: ", data);
		return data;
	}

	/**
	 * Get paginated warranty claims data with filtering and sorting
	 * @param {Object} paginationParams - Pagination parameters from store
	 * @param {Object} filters - Filter parameters from store
	 * @param {Object} sorting - Sorting parameters from store (optional)
	 * @returns {Promise<{data: Array, pagination: Object}>}
	 */
	static async getPaginatedWarrantyData(paginationParams = {}, filters = {}, sorting = null) {
		const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams;

		// Use sorting from the dedicated sorting parameter first, then fall back to pagination params
		const actualSortField = sorting?.field || sortField;
		const actualSortOrder = sorting?.order || sortOrder;

		const options = {
			page: page + 1, // Backend expects 1-based pages
			page_size: pageSize,
			filters,
			sorting:
				actualSortField && actualSortOrder
					? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}`
					: null,
			for_table: true,
		};

		console.log("DEBUG: API - Sending warranty options to backend:", options);

		const result = await this.request(FRAPPE_GET_WARRANTY_CLAIMS_METHOD, { options });
		return result;
	}

	static async createWarranty(warrantyData) {
		const payload = DataUtils.toSnakeCaseObject(warrantyData);
		const result = await this.request(FRAPPE_UPSERT_INVOICE_METHOD, { data: payload });
		console.log("DEBUG: API - Created Warranty: ", result);
		return result;
	}

	// ============================================================================
	// ADDRESS METHODS
	// ============================================================================

	static async getAddressByFullAddress(fullAddress) {
		return await this.request("custom_ui.api.db.addresses.get_address_by_full_address", {
			full_address: fullAddress,
		});
	}

	static async getAddress(fullAddress) {
		return await this.request("custom_ui.api.db.addresses.get_address", { fullAddress });
	}

	static async getContactsForAddress(fullAddress) {
		return await this.request("custom_ui.api.db.addresses.get_contacts_for_address", {
			fullAddress,
		});
	}

	static async searchAddresses(searchTerm) {
		const filters = {
			full_address: ["like", `%${searchTerm}%`],
		};
		return await this.getAddresses(["full_address"], filters);
	}

	static async getAddresses(fields = ["*"], filters = {}) {
		return await this.request(FRAPPE_GET_ADDRESSES_METHOD, { fields, filters });
	}

	// ============================================================================
	// ITEM/PACKAGE METHODS
	// ============================================================================

	static async getItemsByProjectTemplate(projectTemplate) {
		return await this.request(FRAPPE_GET_ITEMS_BY_PROJECT_TEMPLATE_METHOD, { projectTemplate });
	}

	static async saveAsPackageItem(data) {
		return await this.request(FRAPPE_SAVE_AS_PACKAGE_ITEM_METHOD, { data });
	}

	static async getItemCategories() {
		return await this.request("custom_ui.api.db.items.get_item_categories");
	}

	// ============================================================================
	// SERVICE / ROUTE / TIMESHEET METHODS
	// ============================================================================

	static async getServiceData() {
		const data = DataUtils.dummyServiceData;
		return data;
	}

	static async getRouteData() {
		const data = DataUtils.dummyRouteData;
		//const data = [];
		const routes = getDocList("Pre-Built Routes");
		for (const rt of routes) {
			route = getDetailedDoc("Pre-Built Routes", rt.name);
			let tableRow = {};
		}
		return data;
	}

	static async getTimesheetData() {
		//const data = DataUtils.dummyTimesheetData;
		const data = [];
		const timesheets = await this.getDocsList("Timesheet");
		for (const ts of timesheets) {
			const timesheet = await this.getDetailedDoc("Timesheet", ts.name);
			const tableRow = {
				timesheetId: timesheet.name,
				employee: timesheet.employee_name,
				date: timesheet.date,
				customer: timesheet.customer,
				totalHours: timesheet.total_hours,
				status: timesheet.status,
				totalPay: timesheet.total_costing_amount,
			};
			console.log("Timesheet Row: ", tableRow);
			data.push(tableRow);
		}
		console.log("DEBUG: API - getTimesheetData result: ", data);
		return data;
	}

	// ============================================================================
	// EMPLOYEE METHODS
	// ============================================================================

	static async getEmployees(company, roles = []) {
		return await this.request(FRAPPE_GET_EMPLOYEES_METHOD, { company, roles });
	}

	static async getEmployeesOrganized (company, roles = []) {
		return await this.request(FRAPPE_GET_EMPLOYEES_ORGANIZED_METHOD, { company, roles });
	}

	// ============================================================================
	// OTHER METHODS
	// ============================================================================

	static async getWeekHolidays(startDate) {
		return await this.request(FRAPPE_GET_WEEK_HOLIDAYS_METHOD, { weekStartDate: startDate });
	}

	// ============================================================================
	// GENERIC DOCTYPE METHODS
	// ============================================================================

	/**
	 * Fetch a list of documents from a specific doctype.
	 *
	 * @param {String} doctype
	 * @param {string[]} fields
	 * @param {Object} filters
	 * @returns {Promise<Object[]>}
	 */
	static async getDocsList(
		doctype,
		fields = ["*"],
		filters = {},
		pluck = null,
	) {
		const docs = await this.request(
			FRAPPE_GET_DOC_LIST_METHOD,
			{
				doctype, 
				fields,
				filters,
				pluck,
			}
		);
		console.log(
			`DEBUG: API - Fetched ${doctype} list: `,
			docs,
		);
		return docs;
	}

	/**
	 * Fetch a detailed document by doctype and name.
	 *
	 * @param {String} doctype
	 * @param {String} name
	 * @param {Object} filters
	 * @returns {Promise<Object>}
	 */
	static async getDetailedDoc(doctype, name, filters = {}) {
		const doc = await frappe.db.get_doc(doctype, name, filters);
		console.log(`DEBUG: API - Fetched Detailed ${doctype}: `, doc);
		return doc;
	}

	static async getDocCount(doctype, filters = {}) {
		const count = await frappe.db.count(doctype, filters);
		console.log(`DEBUG: API - Counted ${doctype}: `, count);
		return count;
	}

	static async createDoc(doctype, data) {
		const doc = await frappe.db.insert({
			...data,
			doctype,
		});
		console.log(`DEBUG: API - Created ${doctype}: `, doc);
		return doc;
	}

	static async getCompanyNames() {
		const companies = await this.getDocsList("Company", ["name"]);
		const companyNames = companies.map((company) => company.name);
		console.log("DEBUG: API - Fetched Company Names: ", companyNames);
		return companyNames;
	}

	// ============================================================================
	// EXTERNAL API METHODS
	// ============================================================================

	/**
	 * Fetch a list of places (city/state) by zipcode using Zippopotamus API.
	 *
	 * @param {String} zipcode
	 * @returns {Promise<Object[]>}
	 */
	static async getCityStateByZip(zipcode) {
		const url = `${ZIPPOPOTAMUS_BASE_URL}/${zipcode}`;
		const response = await this.request(FRAPPE_PROXY_METHOD, { url, method: "GET" });
		const { places } = response || {};
		if (!places || places.length === 0) {
			throw new Error(`No location data found for zip code ${zipcode}`);
		}
		return places.map((place) => ({
			city: place["place name"],
			state: place["state abbreviation"],
		}));
	}

	static async getGeocode(address) {
		const urlSafeAddress = encodeURIComponent(address);
		const url = `https://nominatim.openstreetmap.org/search?format=jsonv2&q=${urlSafeAddress}`;
		const response = await this.request(FRAPPE_PROXY_METHOD, {
			url,
			method: "GET",
			headers: { "User-Agent": "FrappeApp/1.0 (+https://yourappdomain.com)" },
		});
		const { lat, lon } = response[0] || {};
		return { latitude: parseFloat(lat), longitude: parseFloat(lon) };
	}
}

export default Api;
