<template>
	<Dialog
		v-model="show"
		:options="{
			size: '2xl',
		}"
	>
		<template #body-title>
			<div class="flex items-center justify-between space-x-2 text-base w-full">
				<div class="text-xl font-semibold text-ink-gray-9">
					{{
						programName === 'new' ? __('Create Program') : __('Edit Program')
					}}
				</div>
				<Badge theme="orange" v-if="dirty">
					{{ __('Not Saved') }}
				</Badge>
			</div>
		</template>
		<template #body-content>
			<div class="text-base">
				<div class="grid grid-cols-1 md:grid-cols-2 gap-5 pb-5">
					<FormControl
						v-model="program.name"
						:label="__('Title')"
						type="text"
						:required="true"
						@change="dirty = true"
					/>
					<div class="flex flex-col space-y-3">
						<FormControl
							v-model="program.published"
							:label="__('Published')"
							type="checkbox"
							@change="dirty = true"
						/>
						<FormControl
							v-model="program.enforce_course_order"
							:label="__('Enforce Course Order')"
							type="checkbox"
							@change="dirty = true"
						/>
					</div>
				</div>

				<div class="pb-5">
					<div class="flex items-center justify-between mt-5 mb-4">
						<div class="text-lg font-semibold text-ink-gray-9">
							{{ __('Courses') }}
						</div>
						<Button @click="openForm('course')">
							<template #prefix>
								<Plus class="h-4 w-4 stroke-1.5" />
							</template>
							<span>
								{{ __('Add') }}
							</span>
						</Button>
					</div>
					<ListView
						v-if="program.program_courses?.length > 0"
						:columns="courseColumns"
						:rows="program.program_courses"
						:options="{
							selectable: true,
							resizeColumn: true,
							showTooltip: false,
						}"
						:rowKey="programName === 'new' ? 'course' : 'name'"
					>
						<ListHeader
							class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
						>
							<ListHeaderItem :item="item" v-for="item in courseColumns" />
						</ListHeader>
						<ListRows>
							<Draggable
								:list="program.program_courses"
								:item-key="programName === 'new' ? 'course' : 'name'"
								group="items"
								@end="updateOrder"
								class="cursor-move"
							>
								<template #item="{ element: row }">
									<ListRow :row="row" />
								</template>
							</Draggable>
						</ListRows>
						<ListSelectBanner>
							<template #actions="{ unselectAll, selections }">
								<div class="flex gap-2">
									<Button
										variant="ghost"
										@click="remove(selections, unselectAll, 'courses')"
									>
										<Trash2 class="h-4 w-4 stroke-1.5" />
									</Button>
								</div>
							</template>
						</ListSelectBanner>
					</ListView>
					<div v-else class="text-ink-gray-7">
						{{ __('No courses added yet.') }}
					</div>
				</div>

				<div>
					<div class="flex items-center justify-between mt-5 mb-4">
						<div class="text-lg font-semibold text-ink-gray-9">
							{{ __('Members') }}
						</div>

						<div class="space-x-2">
							<Button
								v-if="programMembers.data.length > 0"
								@click="
									() => {
										showProgressDialog = true
									}
								"
							>
								<template #prefix>
									<TrendingUp class="size-4 stroke-1.5" />
								</template>
								{{ __('Progress Summary') }}
							</Button>
							<Button @click="openForm('member')">
								<template #prefix>
									<Plus class="h-4 w-4 stroke-1.5" />
								</template>
								{{ __('Add') }}
							</Button>
						</div>
					</div>
					<ListView
						v-if="program.program_members?.length > 0"
						:columns="memberColumns"
						:rows="program.program_members"
						:options="{
							selectable: true,
							resizeColumn: true,
						}"
						:rowKey="programName === 'new' ? 'member' : 'name'"
					>
						<ListHeader
							class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
						>
							<ListHeaderItem :item="item" v-for="item in memberColumns" />
						</ListHeader>
						<ListRows>
							<ListRow :row="row" v-for="row in program.program_members" />
						</ListRows>
						<ListSelectBanner>
							<template #actions="{ unselectAll, selections }">
								<div class="flex gap-2">
									<Button
										variant="ghost"
										@click="remove(selections, unselectAll, 'members')"
									>
										<Trash2 class="h-4 w-4 stroke-1.5" />
									</Button>
								</div>
							</template>
						</ListSelectBanner>
					</ListView>
					<div v-else class="text-ink-gray-7">
						{{ __('No members added yet.') }}
					</div>
				</div>
			</div>
			<Dialog
				v-model="showFormDialog"
				:options="{
					title:
						currentForm == 'course'
							? __('Add Course to Program')
							: __('Enroll Member to Program'),
					actions: [
						{
							label: __('Add'),
							variant: 'solid',
							onClick: ({ close }: { close: () => void }) =>
								currentForm == 'course'
									? addCourse(close)
									: addMember(close),
						},
					],
				}"
			>
				<template #body-content>
					<div @click.stop>
						<Link
							v-if="currentForm == 'course'"
							v-model="course"
							doctype="LMS Course"
							:label="__('Course')"
						/>

						<Link
							v-if="currentForm == 'member'"
							v-model="member"
							doctype="User"
							:filters="{
								ignore_user_type: 1,
							}"
							:label="__('Program Member')"
							:onCreate="(value: string, close: () => void) => openSettings('Members', close)"
						/>
					</div>
				</template>
			</Dialog>

			<ProgramProgressSummary
				v-model="showProgressDialog"
				:programName="programName"
				:programMembers="programMembers.data"
			/>
		</template>
		<template #actions="{ close }">
			<div class="flex justify-end space-x-2">
				<Button
					v-if="programName != 'new'"
					@click="deleteProgram(close)"
					variant="outline"
					theme="red"
				>
					<template #prefix>
						<Trash2 class="size-4 stroke-1.5" />
					</template>
					{{ __('Delete') }}
				</Button>
				<Button variant="solid" @click="saveProgram(close)">
					{{ __('Save') }}
				</Button>
			</div>
		</template>
	</Dialog>
</template>
<script setup lang="ts">
import {
	Badge,
	Button,
	createListResource,
	Dialog,
	FormControl,
	ListSelectBanner,
	ListView,
	ListHeader,
	ListHeaderItem,
	ListRows,
	ListRow,
	toast,
} from 'frappe-ui'
import { computed, ref, watch, getCurrentInstance } from 'vue'
import { Plus, Trash2, TrendingUp } from 'lucide-vue-next'
import { Programs, Program } from '@/types/programs'
import { escapeHTML, openSettings } from '@/utils'
import Link from '@/components/Controls/Link.vue'
import Draggable from 'vuedraggable'
import ProgramProgressSummary from '@/pages/Programs/ProgramProgressSummary.vue'

const show = defineModel<boolean>()
const programs = defineModel<Programs>('programs')
const showFormDialog = ref(false)
const currentForm = ref<'course' | 'member'>('course')
const course = ref<string>('')
const member = ref<string>('')
const showProgressDialog = ref(false)
const dirty = ref(false)

const app = getCurrentInstance()
const { $dialog } = app.appContext.config.globalProperties

const props = withDefaults(
	defineProps<{
		programName: string | null
	}>(),
	{
		programName: 'new',
	}
)

const program = ref<Program>({
	name: '',
	title: '',
	published: false,
	enforce_course_order: false,
	program_courses: [],
	program_members: [],
})

watch(
	() => props.programName,
	() => {
		setProgramData()
		fetchCourses()
		fetchMembers()
	}
)

const setProgramData = () => {
	let isNew = true
	programs.value?.data.forEach((p: Program) => {
		if (p.name === props.programName) {
			isNew = false
			program.value = { ...p }
		}
	})

	if (isNew) {
		program.value = {
			name: '',
			title: '',
			published: false,
			enforce_course_order: false,
			program_courses: [],
			program_members: [],
		}
	}
	dirty.value = false
}

const programCourses = createListResource({
	doctype: 'LMS Program Course',
	fields: ['course', 'course_title', 'name', 'idx'],
	cache: ['programCourses', props.programName],
	parent: 'LMS Program',
	orderBy: 'idx',
	onSuccess(data: ProgramCourse[]) {
		program.value.program_courses = data
	},
})

const programMembers = createListResource({
	doctype: 'LMS Program Member',
	fields: ['member', 'full_name', 'progress', 'name'],
	cache: ['programMembers', props.programName],
	parent: 'LMS Program',
	orderBy: 'creation desc',
	onSuccess(data: ProgramMember[]) {
		program.value.program_members = data
	},
})

const fetchCourses = () => {
	programCourses.update({
		filters: {
			parent: props.programName,
			parenttype: 'LMS Program',
			parentfield: 'program_courses',
		},
	})
	programCourses.reload()
}

const fetchMembers = () => {
	programMembers.update({
		filters: {
			parent: props.programName,
			parenttype: 'LMS Program',
			parentfield: 'program_members',
		},
	})
	programMembers.reload()
}

const validateTitle = () => {
	program.value.name = escapeHTML(program.value.name.trim())
}

const saveProgram = (close: () => void) => {
	validateTitle()
	if (props.programName === 'new') createNewProgram(close)
	else updateProgram(close)
	dirty.value = false
}

const createNewProgram = (close: () => void) => {
	programs.value.insert.submit(
		{
			...program.value,
			title: program.value.name,
		},
		{
			onSuccess() {
				close()
				programs.value.reload()
				toast.success(__('Program created successfully'))
			},
			onError(err: any) {
				toast.warning(__(err.messages?.[0] || err))
			},
		}
	)
}

const updateProgram = (close: () => void) => {
	programs.value.setValue.submit(
		{
			name: props.programName,
			...program.value,
		},
		{
			onSuccess() {
				close()
				programs.value.reload()
				toast.success(__('Program updated successfully'))
			},
			onError(err: any) {
				toast.warning(__(err.messages?.[0] || err))
			},
		}
	)
}

const openForm = (formType: 'course' | 'member') => {
	currentForm.value = formType
	showFormDialog.value = true
	if (formType === 'course') {
		course.value = ''
	} else {
		member.value = ''
	}
}

const addCourse = (close: () => void) => {
	if (!course.value) {
		toast.warning(__('Please select a course'))
		return
	}

	const existingCourse = program.value.program_courses.find(
		(c: any) => c.course === course.value
	)
	if (!existingCourse) {
		program.value.program_courses.push({
			course: course.value,
			idx: program.value.program_courses.length + 1,
		})
		if (props.programName !== 'new') {
			dirty.value = true
		}
		close()
		toast.success(__('Course added to program successfully'))
	} else {
		toast.warning(__('Course already added to program'))
	}
}

const addMember = (close: () => void) => {
	if (!member.value) {
		toast.warning(__('Please select a member'))
		return
	}

	const existingMember = program.value.program_members.find(
		(m) => m.member === member.value
	)
	if (!existingMember) {
		program.value.program_members.push({
			member: member.value,
		})
		if (props.programName !== 'new') {
			dirty.value = true
		}
		close()
		toast.success(__('Member added to program successfully'))
	} else {
		toast.warning(__('Member already added to program'))
	}
}

const updateCounts = async (
	type: 'member' | 'course',
	action: 'add' | 'remove'
) => {
	if (!props.programName) return

	let memberCount = programMembers.data?.length || 0
	let courseCount = programCourses.data?.length || 0

	if (type === 'member') {
		memberCount += action === 'add' ? 1 : -1
	} else {
		courseCount += action === 'add' ? 1 : -1
	}

	await programs.value.setValue.submit(
		{
			name: props.programName,
			member_count: memberCount,
			course_count: courseCount,
		},
		{
			onSuccess() {
				setProgramData()
			},
			onError(err: any) {
				toast.warning(__(err.messages?.[0] || err))
			},
		}
	)
}

const updateOrder = async (e: DragEvent) => {
	let sourceIdx = e.from.dataset.idx
	let targetIdx = e.to.dataset.idx

	if (props.programName === 'new') {
		let courses = program.value.program_courses
		courses.splice(targetIdx, 0, courses.splice(sourceIdx, 1)[0])
		courses.forEach((course, index) => {
			course.idx = index + 1
		})
		dirty.value = true
	} else {
		let courses = programCourses.data
		courses.splice(targetIdx, 0, courses.splice(sourceIdx, 1)[0])

		for (const [index, course] of courses.entries()) {
			programCourses.setValue.submit(
				{
					name: course.name,
					idx: index + 1,
				},
				{
					onError(err: any) {
						toast.warning(__(err.messages?.[0] || err))
					},
				}
			)
			await wait(100)
		}
	}
}

const wait = (ms: number) => new Promise((res) => setTimeout(res, ms))

const remove = (
	selections: string[],
	unselectAll: () => void,
	type: string
) => {
	const selectionsArray = Array.from(selections)
	if (type === 'courses') {
		program.value.program_courses = program.value.program_courses.filter(
			(c: any) => !selectionsArray.includes(c.name || c.course)
		)
	} else {
		program.value.program_members = program.value.program_members.filter(
			(m: any) => !selectionsArray.includes(m.name || m.member)
		)
	}
	dirty.value = true
	unselectAll()
}

const deleteProgram = (close: () => void) => {
	if (props.programName == 'new') return
	$dialog({
		title: __('Delete Program'),
		message: __(
			'Are you sure you want to delete this program? This action cannot be undone.'
		),
		actions: [
			{
				label: __('Delete'),
				theme: 'red',
				variant: 'solid',
				onClick(closeDialog) {
					programs.value?.delete.submit(props.programName, {
						onSuccess() {
							toast.success(__('Program deleted successfully'))
							close()
							closeDialog()
						},
						onError(err: any) {
							toast.warning(__(err.messages?.[0] || err))
							closeDialog()
						},
					})
				},
			},
		],
	})
}

const courseColumns = computed(() => {
	return [
		{
			label: 'Title',
			key: props.programName === 'new' ? 'course' : 'course_title',
			width: 1,
		},
	]
})

const memberColumns = computed(() => {
	return [
		{
			label: 'Member',
			key: 'member',
			width: 3,
			align: 'left',
		},
		{
			label: 'Full Name',
			key: 'full_name',
			width: 3,
			align: 'left',
		},
	]
})
</script>
