import { NgStyle } from '@angular/common'
import { Component, computed, inject, input } from '@angular/core'
import { RouterOutlet } from '@angular/router'
import { removeNulls, sortTablesByRoomPlan } from '@core/utils'
import { ReservationResponse, TableGraphResponse, TableResponse } from '@api-client/angular'
import { TranslocoDirective } from '@jsverse/transloco'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import LinkerLine, { LinkerLineOptions } from 'linkerline'
import { TABLE_CONSTANTS } from '../../../core'
import { calculateRoomPlanCoordinates } from '../../../shared'
import { DashboardStore } from '../dashboard.store'
import { isLateReservation } from '../utils'
import { RoomPlanTableComponent } from './table/room-plan-table.component'

/** Room Plan displays all tables of the restaurant in a grid */
@UntilDestroy({ checkProperties: true })
@Component({
	selector: 'kadi-room-plan',
	templateUrl: './room-plan.component.html',
	imports: [TranslocoDirective, NgStyle, RoomPlanTableComponent, RouterOutlet],
})
export class RoomPlanComponent {
	private readonly _store = inject(DashboardStore)

	readonly isScreenSmall = input(false)

	POINT_LENGTH = TABLE_CONSTANTS.TABLE_POINT_LENGTH
	lineOptions: Partial<LinkerLineOptions<HTMLElement, HTMLElement>> = {
		size: 4,
		path: 'fluid',
		startPlug: 'behind',
		endPlug: 'behind',
		startPlugSize: 0,
		endPlugSize: 0,
	}
	lines: LinkerLine<HTMLElement, HTMLElement>[] = []

	state = this._store.state
	coordinates = computed(() => calculateRoomPlanCoordinates(this.state().tables.map((table) => table.tableRef)))

	constructor() {
		this._store.dashboardTables$.pipe(untilDestroyed(this)).subscribe(() => {
			for (const line of this.lines) line.remove()
			this.drawLines()
		})
	}

	private drawLines() {
		const graph = this.state().tableGraph as TableGraphResponse

		const reservations = this.state()
			.tables.filter((table) => table.reservation && table.reservation?.tableIds.length > 1)
			.map((table) => table.reservation)
			.filter(removeNulls)

		for (const reservation of reservations) {
			const connections = this.getConnectionsToDraw(reservation.tableIds, graph)

			if (!connections) continue

			for (const [leftTableId, rightTableIds] of connections) {
				const start = document.getElementById(leftTableId)!
				const end = document.getElementById(rightTableIds[0])!

				const color = this.getReservationStateColor(reservation)

				const line = new LinkerLine({ start, end, ...this.lineOptions, color })
				this.lines.push(line)
			}
		}
	}

	private getConnectionsToDraw(tableIds: string[], graph: TableGraphResponse) {
		if (!graph) return

		const connections = new Map<string, string[]>()

		const areConnected = (leftTable: string, rightTable: string) =>
			connections.get(leftTable)?.includes(rightTable) || connections.get(rightTable)?.includes(leftTable)

		let tables = this.state()
			.tables.filter((table) => tableIds.includes(table.tableRef.id))
			.map((table) => table.tableRef)

		tables = sortTablesByRoomPlan(tables)

		for (const table of tables) {
			const edges = graph.edges[table.id]?.filter((id) => tableIds.includes(id))
			if (!edges) continue

			for (const edge of edges) {
				const edgeTable = tables.find((_table) => _table.id === edge)!
				if (!this.areTablesDiagonal(table, edgeTable) && !areConnected(table.id, edge)) {
					connections.set(table.id, [edge])
				}
			}
		}

		return connections
	}

	private areTablesDiagonal(tableA: TableResponse, tableB: TableResponse): boolean {
		return tableA.columnStart !== tableB.columnStart && tableA.rowStart !== tableB.rowStart
	}

	private getReservationStateColor(reservation: ReservationResponse): string {
		if (isLateReservation(reservation)) return '#d95757'

		switch (reservation.state) {
			case ReservationResponse.StateEnum.unconfirmed: {
				return '#f08c00'
			}
			case ReservationResponse.StateEnum.approved: {
				return '#828ade'
			}
			case ReservationResponse.StateEnum.seated: {
				return '#2f9e44'
			}
			case ReservationResponse.StateEnum.rejected: {
				return '#6c757d'
			}
			case ReservationResponse.StateEnum.done: {
				return '#2d6a4f'
			}
		}
	}
}
