import os
import time

import frappe

from shipping_bill_extractor.services.pdf_analyzer import PDFAnalyzer
from shipping_bill_extractor.services.shipping_bill_parser import ShippingBillParser


@frappe.whitelist()
def analyze_shipping_bill(file_url):
	"""
	Analyze an uploaded Shipping Bill PDF and return structured JSON.

	Flow:
		Uploaded Frappe File
			-> PDFAnalyzer
			-> pdfplumber text/words/tables
			-> ShippingBillParser
			-> shipping_bill_json

	No AI is used.
	"""

	start_time = time.perf_counter()

	# ---------------------------------------------------------
	# 1. VALIDATE REQUEST
	# ---------------------------------------------------------

	if not file_url:
		frappe.throw("PDF file URL is required.")

	if not isinstance(file_url, str):
		frappe.throw("Invalid PDF file URL.")

	file_url = file_url.strip()

	if not file_url:
		frappe.throw("PDF file URL is required.")

	if not file_url.lower().endswith(".pdf"):
		frappe.throw("Only PDF files are supported.")

	# ---------------------------------------------------------
	# 2. FIND FRAPPE FILE
	# ---------------------------------------------------------

	try:
		file_doc = frappe.get_doc(
			"File",
			{"file_url": file_url},
		)
	except Exception:
		frappe.log_error(
			frappe.get_traceback(),
			"Shipping Bill File Lookup Error",
		)
		frappe.throw("Unable to find the uploaded PDF file.")

	if not file_doc:
		frappe.throw("Uploaded PDF file does not exist.")

	file_path = file_doc.get_full_path()

	if not file_path:
		frappe.throw("Unable to determine the uploaded PDF path.")

	if not os.path.exists(file_path):
		frappe.throw("Uploaded PDF file was not found on the server.")

	if not os.path.isfile(file_path):
		frappe.throw("Uploaded PDF path is not a valid file.")

	if not file_path.lower().endswith(".pdf"):
		frappe.throw("Only PDF files are supported.")

	# ---------------------------------------------------------
	# 3. ANALYZE PDF
	# ---------------------------------------------------------

	try:
		analyzer = PDFAnalyzer(file_path)
		analysis = analyzer.analyze()

	except Exception as exc:
		frappe.log_error(
			frappe.get_traceback(),
			"Shipping Bill PDF Analysis Error",
		)

		frappe.throw(f"Unable to analyze the uploaded PDF: {str(exc)}")

	# ---------------------------------------------------------
	# 4. VALIDATE ANALYZER RESULT
	# ---------------------------------------------------------

	if not isinstance(analysis, dict):
		frappe.throw("PDF analyzer returned an invalid response.")

	pages = analysis.get("pages") or []

	if not pages:
		frappe.throw("No readable pages were found in the uploaded PDF.")

	total_pages = analysis.get(
		"total_pages",
		len(pages),
	)

	text_pages = analysis.get(
		"text_pages",
		0,
	)

	image_pages = analysis.get(
		"image_pages",
		0,
	)

	# ---------------------------------------------------------
	# 5. PARSE SHIPPING BILL
	# ---------------------------------------------------------

	try:
		parser = ShippingBillParser(pages)
		parsed = parser.parse()

	except Exception as exc:
		frappe.log_error(
			frappe.get_traceback(),
			"Shipping Bill Parser Error",
		)

		frappe.throw(f"Unable to extract Shipping Bill data from the PDF: {str(exc)}")

	# ---------------------------------------------------------
	# 6. VALIDATE PARSER RESULT
	# ---------------------------------------------------------

	if not isinstance(parsed, dict):
		frappe.throw("Shipping Bill parser returned an invalid response.")

	shipping_bill_json = parsed.get("shipping_bill_json")

	if not isinstance(shipping_bill_json, dict):
		frappe.throw("Shipping Bill parser did not return valid JSON data.")

	trace = parsed.get(
		"_trace",
		[],
	)

	if not isinstance(trace, list):
		trace = []

	# ---------------------------------------------------------
	# 7. BUILD LIGHTWEIGHT API RESPONSE
	# ---------------------------------------------------------

	processing_time = round(
		time.perf_counter() - start_time,
		2,
	)

	return {
		"shipping_bill_json": shipping_bill_json,
		"file_id": file_doc.name,
		"file_name": file_doc.file_name,
		"file_url": file_doc.file_url,
		"processing_time": processing_time,
		"pdf_analysis": {
			"total_pages": total_pages,
			"pdf_type": analysis.get(
				"pdf_type",
				"",
			),
			"text_pages": text_pages,
			"image_pages": image_pages,
			"total_words": analysis.get(
				"total_words",
				0,
			),
			"total_tables": analysis.get(
				"total_tables",
				0,
			),
		},
		"trace": trace,
	}


# -------------------------------------------------------------
# COURIER BILL API
# -------------------------------------------------------------

@frappe.whitelist()
def analyze_courier_bill(file_url):
	"""
	Analyze an uploaded Courier Bill PDF and return structured JSON.

	Flow:
		Uploaded Frappe File
			-> PDFAnalyzer
			-> pdfplumber text/words/tables
			-> CourierBillParser
			-> courier_bill_json

	No AI is used.

	This function is added independently so the existing
	Shipping Bill API remains unchanged.
	"""

	start_time = time.perf_counter()

	# ---------------------------------------------------------
	# 1. VALIDATE REQUEST
	# ---------------------------------------------------------

	if not file_url:
		frappe.throw("PDF file URL is required.")

	if not isinstance(file_url, str):
		frappe.throw("Invalid PDF file URL.")

	file_url = file_url.strip()

	if not file_url:
		frappe.throw("PDF file URL is required.")

	if not file_url.lower().endswith(".pdf"):
		frappe.throw("Only PDF files are supported.")

	# ---------------------------------------------------------
	# 2. FIND FRAPPE FILE
	# ---------------------------------------------------------

	try:
		file_doc = frappe.get_doc(
			"File",
			{"file_url": file_url},
		)
	except Exception:
		frappe.log_error(
			frappe.get_traceback(),
			"Courier Bill File Lookup Error",
		)
		frappe.throw("Unable to find the uploaded PDF file.")

	if not file_doc:
		frappe.throw("Uploaded PDF file does not exist.")

	file_path = file_doc.get_full_path()

	if not file_path:
		frappe.throw("Unable to determine the uploaded PDF path.")

	if not os.path.exists(file_path):
		frappe.throw("Uploaded PDF file was not found on the server.")

	if not os.path.isfile(file_path):
		frappe.throw("Uploaded PDF path is not a valid file.")

	if not file_path.lower().endswith(".pdf"):
		frappe.throw("Only PDF files are supported.")

	# ---------------------------------------------------------
	# 3. ANALYZE PDF
	# ---------------------------------------------------------

	try:
		analyzer = PDFAnalyzer(file_path)
		analysis = analyzer.analyze()

	except Exception as exc:
		frappe.log_error(
			frappe.get_traceback(),
			"Courier Bill PDF Analysis Error",
		)

		frappe.throw(
			f"Unable to analyze the uploaded PDF: {str(exc)}"
		)

	# ---------------------------------------------------------
	# 4. VALIDATE ANALYZER RESULT
	# ---------------------------------------------------------

	if not isinstance(analysis, dict):
		frappe.throw("PDF analyzer returned an invalid response.")

	pages = analysis.get("pages") or []

	if not pages:
		frappe.throw("No readable pages were found in the uploaded PDF.")

	total_pages = analysis.get(
		"total_pages",
		len(pages),
	)

	text_pages = analysis.get(
		"text_pages",
		0,
	)

	image_pages = analysis.get(
		"image_pages",
		0,
	)

	# ---------------------------------------------------------
	# 5. PARSE COURIER BILL
	# ---------------------------------------------------------

	try:
		# Import here so the existing Shipping Bill API and its
		# current imports/code remain unchanged.
		from shipping_bill_extractor.services.courier_bill_parser import (
			CourierBillParser,
		)

		parser = CourierBillParser(pages)
		parsed = parser.parse()

	except Exception as exc:
		frappe.log_error(
			frappe.get_traceback(),
			"Courier Bill Parser Error",
		)

		frappe.throw(
			f"Unable to extract Courier Bill data from the PDF: {str(exc)}"
		)

	# ---------------------------------------------------------
	# 6. VALIDATE PARSER RESULT
	# ---------------------------------------------------------

	if not isinstance(parsed, dict):
		frappe.throw("Courier Bill parser returned an invalid response.")

	courier_bill_json = parsed.get(
		"courier_bill_json"
	)

	if not isinstance(courier_bill_json, dict):
		frappe.throw(
			"Courier Bill parser did not return valid JSON data."
		)

	trace = parsed.get(
		"_trace",
		[],
	)

	if not isinstance(trace, list):
		trace = []

	# ---------------------------------------------------------
	# 7. BUILD LIGHTWEIGHT API RESPONSE
	# ---------------------------------------------------------

	processing_time = round(
		time.perf_counter() - start_time,
		2,
	)

	return {
		"courier_bill_json": courier_bill_json,
		"file_id": file_doc.name,
		"file_name": file_doc.file_name,
		"file_url": file_doc.file_url,
		"processing_time": processing_time,
		"pdf_analysis": {
			"total_pages": total_pages,
			"pdf_type": analysis.get(
				"pdf_type",
				"",
			),
			"text_pages": text_pages,
			"image_pages": image_pages,
			"total_words": analysis.get(
				"total_words",
				0,
			),
			"total_tables": analysis.get(
				"total_tables",
				0,
			),
		},
		"trace": trace,
	}


# -------------------------------------------------------------
# OPTIONAL DIRECT TEST
# -------------------------------------------------------------

if __name__ == "__main__":
	print("Shipping Bill API loaded successfully")
