import { GetInvoiceStore, UpdateInvoiceStore } from '$houdini';
import { WAVE_ACCESS_TOKEN } from '$env/static/private';
import { PUBLIC_WAVE_BUSINESS_ID } from '$env/static/public';
import { createWaveServerClient } from '$lib/graphql/wave/client';
import {
	GetCustomersDocument,
	GetProductsDocument,
	GetInvoiceDocument,
	type CustomerEdge,
	type Customer,
	type ProductEdge,
	type Product
} from '$lib/graphql/wave/generated';
import {
	createWaveInvoice,
	deleteWaveInvoice,
	sendWaveInvoice,
	updateWaveInvoice,
	approveWaveInvoice
} from '$lib/graphql/wave/mutations';
import { fromGlobalId } from '$lib/utils/relay';
import type { PageServerLoad, Actions } from './$types';
import { error, fail } from '@sveltejs/kit';

export interface WaveCustomer {
	id: string;
	name: string;
	email: string | null;
	isArchived: boolean;
}

export interface WaveProduct {
	id: string;
	name: string;
	description: string | null;
	unitPrice: number;
	isArchived: boolean;
}

export interface WaveInvoiceItem {
	product: { id: string; name: string };
	description: string | null;
	quantity: number;
	unitPrice: number;
	subtotal: { value: string };
	total: { value: string };
}

export interface WaveInvoice {
	id: string;
	invoiceNumber: string;
	invoiceDate: string;
	dueDate: string;
	status: string;
	title: string;
	subhead: string | null;
	poNumber: string | null;
	memo: string | null;
	footer: string | null;
	pdfUrl: string;
	viewUrl: string;
	customer: { id: string; name: string; email: string | null };
	items: WaveInvoiceItem[];
	subtotal: { value: string; currency: { code: string; symbol: string } };
	taxTotal: { value: string; currency: { code: string; symbol: string } };
	discountTotal: { value: string; currency: { code: string; symbol: string } };
	total: { value: string; currency: { code: string; symbol: string } };
	amountDue: { value: string; currency: { code: string; symbol: string } };
	amountPaid: { value: string; currency: { code: string; symbol: string } };
	lastSentAt: string | null;
	lastSentVia: string | null;
	lastViewedAt: string | null;
}

async function fetchWaveCustomers(): Promise<WaveCustomer[]> {
	const client = createWaveServerClient(WAVE_ACCESS_TOKEN);

	const result = await client.query(GetCustomersDocument, {
		businessId: PUBLIC_WAVE_BUSINESS_ID,
		pageSize: 500
	});

	if (result.error) {
		console.error('Wave API error (customers):', result.error);
		return [];
	}

	const edges = result.data?.business?.customers?.edges ?? [];
	return edges
		.filter((edge: CustomerEdge): edge is CustomerEdge & { node: Customer } => edge.node != null)
		.map((edge: CustomerEdge & { node: Customer }) => ({
			id: edge.node.id,
			name: edge.node.name,
			email: edge.node.email ?? null,
			isArchived: edge.node.isArchived ?? false
		}));
}

async function fetchWaveProducts(): Promise<WaveProduct[]> {
	const client = createWaveServerClient(WAVE_ACCESS_TOKEN);

	const result = await client.query(GetProductsDocument, {
		businessId: PUBLIC_WAVE_BUSINESS_ID,
		pageSize: 500,
		isSold: true,
		isArchived: false
	});

	if (result.error) {
		console.error('Wave API error (products):', result.error);
		return [];
	}

	const edges = result.data?.business?.products?.edges ?? [];
	return edges
		.filter((edge: ProductEdge): edge is ProductEdge & { node: Product } => edge.node != null)
		.map((edge: ProductEdge & { node: Product }) => ({
			id: edge.node.id,
			name: edge.node.name,
			description: edge.node.description ?? null,
			unitPrice: parseFloat(edge.node.unitPrice),
			isArchived: edge.node.isArchived
		}));
}

async function fetchWaveInvoice(waveInvoiceId: string): Promise<WaveInvoice | null> {
	const client = createWaveServerClient(WAVE_ACCESS_TOKEN);

	// The stored waveInvoiceId is decoded (e.g., "businessId;Invoice:invoiceId")
	// Wave API expects base64-encoded Global ID with "Business:" prefix
	const waveGlobalId = btoa(`Business:${waveInvoiceId}`);

	const result = await client.query(GetInvoiceDocument, {
		businessId: PUBLIC_WAVE_BUSINESS_ID,
		invoiceId: waveGlobalId
	});

	if (result.error) {
		console.error('Wave API error (invoice):', result.error);
		return null;
	}

	const invoice = result.data?.business?.invoice;
	if (!invoice) return null;

	return {
		id: invoice.id,
		invoiceNumber: invoice.invoiceNumber,
		invoiceDate: invoice.invoiceDate,
		dueDate: invoice.dueDate,
		status: invoice.status,
		title: invoice.title,
		subhead: invoice.subhead ?? null,
		poNumber: invoice.poNumber ?? null,
		memo: invoice.memo ?? null,
		footer: invoice.footer ?? null,
		pdfUrl: invoice.pdfUrl,
		viewUrl: invoice.viewUrl,
		customer: {
			id: invoice.customer.id,
			name: invoice.customer.name,
			email: invoice.customer.email ?? null
		},
		items: (invoice.items ?? []).map(
			(item: {
				product: { id: string; name: string };
				description: string | null;
				quantity: string;
				unitPrice: string;
				subtotal: { value: string };
				total: { value: string };
			}) => ({
				product: { id: item.product.id, name: item.product.name },
				description: item.description ?? null,
				quantity: parseFloat(item.quantity),
				unitPrice: parseFloat(item.unitPrice),
				subtotal: { value: item.subtotal.value },
				total: { value: item.total.value }
			})
		),
		subtotal: {
			value: invoice.subtotal.value,
			currency: { code: invoice.subtotal.currency.code, symbol: invoice.subtotal.currency.symbol }
		},
		taxTotal: {
			value: invoice.taxTotal.value,
			currency: { code: invoice.taxTotal.currency.code, symbol: invoice.taxTotal.currency.symbol }
		},
		discountTotal: {
			value: invoice.discountTotal.value,
			currency: {
				code: invoice.discountTotal.currency.code,
				symbol: invoice.discountTotal.currency.symbol
			}
		},
		total: {
			value: invoice.total.value,
			currency: { code: invoice.total.currency.code, symbol: invoice.total.currency.symbol }
		},
		amountDue: {
			value: invoice.amountDue.value,
			currency: { code: invoice.amountDue.currency.code, symbol: invoice.amountDue.currency.symbol }
		},
		amountPaid: {
			value: invoice.amountPaid.value,
			currency: {
				code: invoice.amountPaid.currency.code,
				symbol: invoice.amountPaid.currency.symbol
			}
		},
		lastSentAt: invoice.lastSentAt ?? null,
		lastSentVia: invoice.lastSentVia ?? null,
		lastViewedAt: invoice.lastViewedAt ?? null
	};
}

export const load: PageServerLoad = async (event) => {
	const { invoice: invoiceId } = event.params;
	const { parent } = event;

	const fetchParams = {
		event,
		metadata: { cookie: event.locals.cookie }
	};

	// Fetch the invoice, parent data, and Wave data in parallel
	const [invoiceResult, parentData, waveCustomers, waveProducts] = await Promise.all([
		new GetInvoiceStore().fetch({
			...fetchParams,
			variables: {
				id: invoiceId
			}
		}),
		parent(),
		fetchWaveCustomers(),
		fetchWaveProducts()
	]);

	if (!invoiceResult.data?.invoice) {
		throw error(404, 'Invoice not found');
	}

	const invoice = invoiceResult.data.invoice;

	// Fetch Wave invoice if linked
	let waveInvoice: WaveInvoice | null = null;
	if (invoice.waveInvoiceId) {
		waveInvoice = await fetchWaveInvoice(invoice.waveInvoiceId);
	}

	return {
		invoice,
		customers: parentData.customers,
		customerLookup: parentData.customerLookup,
		accounts: parentData.accounts,
		accountLookup: parentData.accountLookup,
		waveCustomers,
		waveProducts,
		waveInvoice
	};
};

export const actions: Actions = {
	createWaveInvoice: async (event) => {
		const { request, params, locals } = event;
		const formData = await request.formData();
		const invoiceId = params.invoice;
		const customerId = formData.get('customerId') as string;
		const invoiceDate = formData.get('invoiceDate') as string;
		const dueDate = formData.get('dueDate') as string;
		const memo = formData.get('memo') as string | null;
		const footer = formData.get('footer') as string | null;
		const itemsJson = formData.get('items') as string;
		const discountJson = formData.get('discount') as string | null;

		if (!customerId || !itemsJson) {
			return fail(400, { error: 'Missing required fields' });
		}

		let items;
		try {
			items = JSON.parse(itemsJson);
		} catch {
			return fail(400, { error: 'Invalid items data' });
		}

		// Parse discount if provided
		let discounts;
		if (discountJson) {
			try {
				const discount = JSON.parse(discountJson);
				if (discount && discount.value > 0) {
					discounts = [
						{
							discountType: discount.type as 'PERCENTAGE' | 'FIXED',
							name: discount.name || 'Discount',
							...(discount.type === 'PERCENTAGE'
								? { percentage: discount.value.toString() }
								: { amount: discount.value.toString() })
						}
					];
				}
			} catch {
				// Ignore invalid discount data
			}
		}

		const result = await createWaveInvoice({
			businessId: PUBLIC_WAVE_BUSINESS_ID,
			customerId,
			status: 'DRAFT',
			invoiceDate,
			dueDate,
			items,
			discounts,
			memo: memo || undefined,
			footer: footer || undefined
		});

		if (!result.success || !result.invoice) {
			return fail(400, { error: result.error || 'Failed to create Wave invoice' });
		}

		// Update Nexus invoice with the Wave invoice ID
		const waveInvoiceId = fromGlobalId(result.invoice.id);
		console.log('[createWaveInvoice] Wave invoice created:', {
			waveGlobalId: result.invoice.id,
			waveInvoiceId,
			nexusInvoiceId: invoiceId
		});

		try {
			const updateResult = await new UpdateInvoiceStore().mutate(
				{ input: { id: invoiceId, waveInvoiceId } },
				{ event, metadata: { cookie: locals.cookie } }
			);

			console.log('[createWaveInvoice] Update result:', {
				data: updateResult.data,
				errors: updateResult.errors
			});

			if (updateResult.errors?.length) {
				return fail(400, {
					error: `Wave invoice created but failed to link: ${updateResult.errors.map((e) => e.message).join(', ')}`
				});
			}

			return { success: true, waveInvoiceId };
		} catch (err) {
			console.error('[createWaveInvoice] Exception during update:', err);
			return fail(500, {
				error: `Wave invoice created (ID: ${waveInvoiceId}) but failed to save to Nexus: ${err instanceof Error ? err.message : 'Unknown error'}`
			});
		}
	},

	sendWaveInvoice: async (event) => {
		const { request, locals } = event;
		const formData = await request.formData();
		const invoiceId = formData.get('invoiceId') as string;
		const nexusInvoiceId = formData.get('nexusInvoiceId') as string;
		const customerEmail = formData.get('customerEmail') as string;

		if (!invoiceId) {
			return fail(400, { error: 'Missing invoice ID' });
		}

		if (!customerEmail) {
			return fail(400, { error: 'Customer email is required to send invoice' });
		}

		const result = await sendWaveInvoice(invoiceId, [customerEmail]);

		if (!result.success) {
			return fail(400, { error: result.error || 'Failed to send Wave invoice' });
		}

		// Update Nexus invoice status to SENT
		if (nexusInvoiceId) {
			await new UpdateInvoiceStore().mutate(
				{ input: { id: nexusInvoiceId, status: 'SENT' } },
				{ event, metadata: { cookie: locals.cookie } }
			);
		}

		return { success: true };
	},

	approveWaveInvoice: async ({ request }) => {
		const formData = await request.formData();
		const invoiceId = formData.get('invoiceId') as string;

		if (!invoiceId) {
			return fail(400, { error: 'Missing invoice ID' });
		}

		const result = await approveWaveInvoice(invoiceId);

		if (!result.success) {
			return fail(400, { error: result.error || 'Failed to approve Wave invoice' });
		}

		return { success: true };
	},

	updateWaveInvoice: async ({ request }) => {
		const formData = await request.formData();
		const invoiceId = formData.get('invoiceId') as string;
		const invoiceDate = formData.get('invoiceDate') as string;
		const dueDate = formData.get('dueDate') as string;
		const memo = formData.get('memo') as string | null;
		const footer = formData.get('footer') as string | null;
		const itemsJson = formData.get('items') as string;
		const discountJson = formData.get('discount') as string | null;

		if (!invoiceId || !itemsJson) {
			return fail(400, { error: 'Missing required fields' });
		}

		let items;
		try {
			items = JSON.parse(itemsJson);
		} catch {
			return fail(400, { error: 'Invalid items data' });
		}

		// Parse discount if provided
		let discounts;
		if (discountJson) {
			try {
				const discount = JSON.parse(discountJson);
				if (discount && discount.value > 0) {
					discounts = [
						{
							discountType: discount.type as 'PERCENTAGE' | 'FIXED',
							name: discount.name || 'Discount',
							...(discount.type === 'PERCENTAGE'
								? { percentage: discount.value.toString() }
								: { amount: discount.value.toString() })
						}
					];
				}
			} catch {
				// Ignore invalid discount data
			}
		}

		const result = await updateWaveInvoice({
			id: invoiceId,
			invoiceDate,
			dueDate,
			items,
			discounts,
			memo: memo || undefined,
			footer: footer || undefined
		});

		if (!result.success) {
			return fail(400, { error: result.error || 'Failed to update Wave invoice' });
		}

		return { success: true };
	},

	deleteWaveInvoice: async (event) => {
		const { request, locals } = event;
		const formData = await request.formData();
		const waveInvoiceId = formData.get('waveInvoiceId') as string;
		const nexusInvoiceId = formData.get('nexusInvoiceId') as string;

		if (!waveInvoiceId || !nexusInvoiceId) {
			return fail(400, { error: 'Missing required fields' });
		}

		// Delete from Wave
		const result = await deleteWaveInvoice(waveInvoiceId);

		if (!result.success) {
			return fail(400, { error: result.error || 'Failed to delete Wave invoice' });
		}

		// Clear waveInvoiceId on Nexus invoice
		const updateResult = await new UpdateInvoiceStore().mutate(
			{ input: { id: nexusInvoiceId, waveInvoiceId: '' } },
			{ event, metadata: { cookie: locals.cookie } }
		);

		if (updateResult.errors?.length) {
			return fail(400, {
				error: `Wave invoice deleted but failed to unlink from Nexus: ${updateResult.errors.map((e) => e.message).join(', ')}`
			});
		}

		return { success: true };
	}
};
