<svelte:options runes={true} />

<script lang="ts">
	import { page } from '$app/stores'
	import { goto } from '$app/navigation'
	import { createQuery, useQueryClient } from '@tanstack/svelte-query'
	import { crewQueries } from '$lib/api/queries/crew.queries'
	import {
		useRemoveMember,
		useUpdateMembership,
		useDeletePhantom,
		useDeclinePhantomClaim
	} from '$lib/api/mutations/crew.mutations'
	import { crewAdapter } from '$lib/api/adapters/crew.adapter'
	import { crewStore } from '$lib/stores/crew.store.svelte'
	import Button from '$lib/components/ui/Button.svelte'
	import Dialog from '$lib/components/ui/Dialog.svelte'
	import DropdownMenu from '$lib/components/ui/DropdownMenu.svelte'
	import ModalHeader from '$lib/components/ui/ModalHeader.svelte'
	import ModalBody from '$lib/components/ui/ModalBody.svelte'
	import ModalFooter from '$lib/components/ui/ModalFooter.svelte'
	import SettingsRow from '$lib/components/ui/SettingsRow.svelte'
	import Switch from '$lib/components/ui/switch/Switch.svelte'
	import CrewHeader from '$lib/components/crew/CrewHeader.svelte'
	import MemberRow from '$lib/components/crew/MemberRow.svelte'
	import PhantomRow from '$lib/components/crew/PhantomRow.svelte'
	import ScoutUserModal from '$lib/components/crew/ScoutUserModal.svelte'
	import BulkPhantomModal from '$lib/components/crew/BulkPhantomModal.svelte'
	import AssignPhantomModal from '$lib/components/crew/AssignPhantomModal.svelte'
	import ConfirmClaimModal from '$lib/components/crew/ConfirmClaimModal.svelte'
	import { DropdownMenu as DropdownMenuBase } from 'bits-ui'
	import type {
		MemberFilter,
		CrewMembership,
		PhantomPlayer,
		CrewInvitation
	} from '$lib/types/api/crew'
	import type { PageData } from './$types'

	interface Props {
		data: PageData
	}

	let { data }: Props = $props()

	const queryClient = useQueryClient()

	// Get filter from URL
	const filter = $derived<MemberFilter>(
		($page.url.searchParams.get('filter') as MemberFilter) || 'all'
	)

	// Query for members based on filter
	const membersQuery = createQuery(() => crewQueries.members(filter))

	// Query for active members (needed for roster cap check regardless of current filter)
	const activeQuery = createQuery(() => ({
		...crewQueries.members('active'),
		enabled: filter !== 'active' // Only fetch separately if not already viewing active
	}))

	// Query for pending invitations (officers only)
	const invitationsQuery = createQuery(() => ({
		...crewQueries.crewInvitations(crewStore.crew?.id ?? ''),
		enabled: crewStore.isOfficer && !!crewStore.crew?.id
	}))

	// Calculate total active roster size (members + phantoms)
	const activeRosterSize = $derived.by(() => {
		// Use active filter data if viewing active, otherwise use dedicated query
		const activeMembers =
			filter === 'active' ? (membersQuery.data?.members ?? []) : (activeQuery.data?.members ?? [])
		const activePhantoms =
			filter === 'active' ? (membersQuery.data?.phantoms ?? []) : (activeQuery.data?.phantoms ?? [])

		const activeMemberCount = activeMembers.filter((m) => !m.retired).length
		const activePhantomCount = activePhantoms.filter((p) => !p.retired).length
		return activeMemberCount + activePhantomCount
	})

	const isRosterFull = $derived(activeRosterSize >= 30)

	// Mutations
	const removeMemberMutation = useRemoveMember()
	const updateMembershipMutation = useUpdateMembership()
	const deletePhantomMutation = useDeletePhantom()
	const declinePhantomClaimMutation = useDeclinePhantomClaim()

	// Filter options - Pending only shown to officers
	const filterOptions = $derived.by(() => {
		const options: { value: MemberFilter; label: string }[] = [
			{ value: 'all', label: 'All' },
			{ value: 'active', label: 'Active' },
			{ value: 'phantom', label: 'Phantoms' }
		]
		if (crewStore.isOfficer) {
			options.push({ value: 'pending', label: 'Pending' })
		}
		options.push({ value: 'retired', label: 'Retired' })
		return options
	})

	// Change filter
	function handleFilterChange(newFilter: MemberFilter) {
		const url = new URL($page.url)
		if (newFilter === 'all') {
			url.searchParams.delete('filter')
		} else {
			url.searchParams.set('filter', newFilter)
		}
		goto(url.toString(), { replaceState: true })
	}

	// Dialog state for member actions
	let confirmDialogOpen = $state(false)
	let confirmAction = $state<'remove' | 'promote' | 'demote' | null>(null)
	let selectedMember = $state<CrewMembership | null>(null)

	// Dialog state for editing member/phantom
	let editDialogOpen = $state(false)
	let editingMember = $state<CrewMembership | null>(null)
	let editingPhantom = $state<PhantomPlayer | null>(null)
	let editJoinDate = $state('')
	let editRetired = $state(false)

	// Dialog state for scout modal
	let scoutModalOpen = $state(false)

	// Dialog state for phantom creation
	let bulkPhantomDialogOpen = $state(false)

	// Dialog state for phantom deletion confirmation
	let deletePhantomDialogOpen = $state(false)
	let phantomToDelete = $state<PhantomPlayer | null>(null)

	// Dialog state for phantom assignment
	let assignPhantomDialogOpen = $state(false)
	let phantomToAssign = $state<PhantomPlayer | null>(null)

	// Dialog state for confirm claim
	let confirmClaimDialogOpen = $state(false)
	let phantomToClaim = $state<PhantomPlayer | null>(null)

	// Member actions
	function openRemoveDialog(member: CrewMembership) {
		selectedMember = member
		confirmAction = 'remove'
		confirmDialogOpen = true
	}

	function openPromoteDialog(member: CrewMembership) {
		selectedMember = member
		confirmAction = 'promote'
		confirmDialogOpen = true
	}

	function openDemoteDialog(member: CrewMembership) {
		selectedMember = member
		confirmAction = 'demote'
		confirmDialogOpen = true
	}

	async function handleConfirmAction() {
		if (!selectedMember || !crewStore.crew) return

		try {
			if (confirmAction === 'remove') {
				await removeMemberMutation.mutateAsync({
					crewId: crewStore.crew.id,
					membershipId: selectedMember.id
				})
			} else if (confirmAction === 'promote') {
				await updateMembershipMutation.mutateAsync({
					crewId: crewStore.crew.id,
					membershipId: selectedMember.id,
					input: { role: 'vice_captain' }
				})
			} else if (confirmAction === 'demote') {
				await updateMembershipMutation.mutateAsync({
					crewId: crewStore.crew.id,
					membershipId: selectedMember.id,
					input: { role: 'member' }
				})
			}
		} catch (error) {
			console.error('Action failed:', error)
		}

		confirmDialogOpen = false
		selectedMember = null
		confirmAction = null
	}

	// Member/phantom editing
	function openEditMemberDialog(member: CrewMembership) {
		editingMember = member
		editingPhantom = null
		// Format date for input
		editJoinDate = member.joinedAt ? (member.joinedAt.split('T')[0] ?? '') : ''
		editRetired = member.retired
		editDialogOpen = true
	}

	function openEditPhantomDialog(phantom: PhantomPlayer) {
		editingPhantom = phantom
		editingMember = null
		editJoinDate = phantom.joinedAt ? (phantom.joinedAt.split('T')[0] ?? '') : ''
		editRetired = phantom.retired
		editDialogOpen = true
	}

	async function handleSaveEdit() {
		if (!crewStore.crew) return

		try {
			if (editingMember) {
				await updateMembershipMutation.mutateAsync({
					crewId: crewStore.crew.id,
					membershipId: editingMember.id,
					input: { joinedAt: editJoinDate, retired: editRetired }
				})
			} else if (editingPhantom) {
				// Call the phantom update directly through the adapter
				await crewAdapter.updatePhantom(crewStore.crew.id, editingPhantom.id, {
					joinedAt: editJoinDate,
					retired: editRetired
				})
				// Invalidate members query
				membersQuery.refetch()
			}
			// Invalidate GW event queries since membersDuringEvent depends on join dates
			queryClient.invalidateQueries({ queryKey: ['crew', 'gw'] })
		} catch (error) {
			console.error('Failed to update:', error)
		}

		editDialogOpen = false
		editingMember = null
		editingPhantom = null
		editJoinDate = ''
		editRetired = false
	}

	function openDeletePhantomDialog(phantom: PhantomPlayer) {
		phantomToDelete = phantom
		deletePhantomDialogOpen = true
	}

	async function handleConfirmDeletePhantom() {
		if (!crewStore.crew || !phantomToDelete) return

		try {
			await deletePhantomMutation.mutateAsync({
				crewId: crewStore.crew.id,
				phantomId: phantomToDelete.id
			})
		} catch (error) {
			console.error('Failed to delete phantom:', error)
		}

		deletePhantomDialogOpen = false
		phantomToDelete = null
	}

	// Phantom assignment
	function openAssignPhantomDialog(phantom: PhantomPlayer) {
		phantomToAssign = phantom
		assignPhantomDialogOpen = true
	}

	// Confirm claim (opens modal)
	function openConfirmClaimDialog(phantom: PhantomPlayer) {
		phantomToClaim = phantom
		confirmClaimDialogOpen = true
	}

	// Decline claim (direct action, no confirmation needed)
	async function handleDeclineClaim(phantom: PhantomPlayer) {
		if (!crewStore.crew) return

		try {
			await declinePhantomClaimMutation.mutateAsync({
				crewId: crewStore.crew.id,
				phantomId: phantom.id
			})
		} catch (error) {
			console.error('Failed to decline phantom claim:', error)
		}
	}

	// Format date
	function formatDate(dateString: string): string {
		return new Date(dateString).toLocaleDateString(undefined, {
			year: 'numeric',
			month: 'short',
			day: 'numeric'
		})
	}

	// Check if invitation is expired
	function isInvitationExpired(expiresAt: string): boolean {
		return new Date(expiresAt) < new Date()
	}

	// Get pending (non-expired) invitations count
	const pendingInvitationsCount = $derived(
		invitationsQuery.data?.filter((inv) => !isInvitationExpired(inv.expiresAt)).length ?? 0
	)

	// Get phantoms with pending claims (assigned but not confirmed)
	const pendingClaimPhantoms = $derived(
		membersQuery.data?.phantoms?.filter((p) => p.claimedBy && !p.claimConfirmed) ?? []
	)
</script>

<svelte:head>
	<title>Crew Members / granblue.team</title>
</svelte:head>

<div class="page">
	<div class="card">
		<CrewHeader title="Members" backHref="/crew">
			{#snippet belowTitle()}
				<div class="filter-tabs">
					{#each filterOptions as option}
						<button
							class="filter-tab"
							class:active={filter === option.value}
							onclick={() => handleFilterChange(option.value)}
						>
							{option.label}
						</button>
					{/each}
				</div>
			{/snippet}
			{#snippet actions()}
				{#if crewStore.isOfficer}
					<Button
						variant="secondary"
						size="small"
						disabled={isRosterFull}
						onclick={() => (scoutModalOpen = true)}
					>
						Scout
					</Button>
					<DropdownMenu>
						{#snippet trigger({ props })}
							<Button variant="secondary" size="small" iconOnly icon="ellipsis" {...props} />
						{/snippet}
						{#snippet menu()}
							<DropdownMenuBase.Item
								class="dropdown-menu-item"
								onclick={() => (bulkPhantomDialogOpen = true)}
							>
								Add phantoms...
							</DropdownMenuBase.Item>
						{/snippet}
					</DropdownMenu>
				{/if}
			{/snippet}
		</CrewHeader>

		<!-- Pending Invitations (shown when filter is 'pending') -->
		{#if filter === 'pending'}
			{#if invitationsQuery.isLoading}
				<div class="loading-state">
					<p>Loading...</p>
				</div>
			{:else if invitationsQuery.data && invitationsQuery.data.length > 0}
				<ul class="member-list">
					{#each invitationsQuery.data as invitation}
						{@const expired = isInvitationExpired(invitation.expiresAt)}
						<li class="invitation-row" class:expired>
							<div class="invitation-info">
								<span class="invited-user">{invitation.user?.username ?? 'Unknown'}</span>
								{#if invitation.invitedBy}
									<span class="invited-by">
										Invited by {invitation.invitedBy.username}
									</span>
								{/if}
							</div>
							<div class="invitation-status">
								{#if expired}
									<span class="status-badge expired">Expired</span>
								{:else}
									<span class="expires-text">Expires {formatDate(invitation.expiresAt)}</span>
								{/if}
							</div>
						</li>
					{/each}
				</ul>
			{:else}
				<div class="empty-state">
					<p>No pending invitations.</p>
				</div>
			{/if}
		{:else if membersQuery.isLoading}
			<div class="loading-state">
				<p>Loading...</p>
			</div>
		{:else if membersQuery.isError}
			<div class="error-state">
				<p>Failed to load members</p>
			</div>
		{:else}
			<!-- Regular members -->
			{#if membersQuery.data?.members && membersQuery.data.members.length > 0}
				<ul class="member-list">
					{#each membersQuery.data.members as member}
						<MemberRow
							{member}
							onEdit={() => openEditMemberDialog(member)}
							onPromote={() => openPromoteDialog(member)}
							onDemote={() => openDemoteDialog(member)}
							onRemove={() => openRemoveDialog(member)}
						/>
					{/each}
				</ul>
			{:else if filter !== 'phantom'}
				<p class="empty-state">No members found</p>
			{/if}

			<!-- Pending Claims Section (officers only) -->
			{#if crewStore.isOfficer && pendingClaimPhantoms.length > 0 && (filter === 'all' || filter === 'phantom')}
				<div class="section-divider pending-claims">
					<span>Pending Claims ({pendingClaimPhantoms.length})</span>
				</div>
				<ul class="member-list">
					{#each pendingClaimPhantoms as phantom}
						<PhantomRow
							{phantom}
							currentUserId={crewStore.membership?.user?.id}
							onEdit={() => openEditPhantomDialog(phantom)}
							onDelete={() => openDeletePhantomDialog(phantom)}
							onAssign={() => openAssignPhantomDialog(phantom)}
							onAccept={() => openConfirmClaimDialog(phantom)}
							onDecline={() => handleDeclineClaim(phantom)}
						/>
					{/each}
				</ul>
			{/if}

			<!-- Phantom players -->
			{#if membersQuery.data?.phantoms && membersQuery.data.phantoms.length > 0}
				{#if filter === 'all' && membersQuery.data.members.length > 0}
					<div class="section-divider">
						<span>Phantom Players</span>
					</div>
				{/if}
				<ul class="member-list">
					{#each membersQuery.data.phantoms as phantom}
						<PhantomRow
							{phantom}
							currentUserId={crewStore.membership?.user?.id}
							onEdit={() => openEditPhantomDialog(phantom)}
							onDelete={() => openDeletePhantomDialog(phantom)}
							onAssign={() => openAssignPhantomDialog(phantom)}
							onAccept={() => openConfirmClaimDialog(phantom)}
							onDecline={() => handleDeclineClaim(phantom)}
						/>
					{/each}
				</ul>
			{:else if filter === 'phantom'}
				<div class="empty-state">
					<p>No phantom players.</p>
					{#if crewStore.isOfficer}
						<Button variant="secondary" size="small" onclick={() => (bulkPhantomDialogOpen = true)}>
							Add phantoms...
						</Button>
					{/if}
				</div>
			{/if}
		{/if}
	</div>
</div>

<!-- Confirm Action Dialog -->
<Dialog bind:open={confirmDialogOpen}>
	{#snippet children()}
		<ModalHeader
			title={confirmAction === 'remove'
				? 'Remove Member'
				: confirmAction === 'promote'
					? 'Promote to Vice Captain'
					: 'Demote to Member'}
		/>

		<ModalBody>
			<p class="confirm-message">
				{#if confirmAction === 'remove'}
					Are you sure you want to remove {selectedMember?.user?.username ?? 'this member'} from the
					crew?
				{:else if confirmAction === 'promote'}
					Promote {selectedMember?.user?.username ?? 'this member'} to Vice Captain?
				{:else if confirmAction === 'demote'}
					Demote {selectedMember?.user?.username ?? 'this member'} from Vice Captain to Member?
				{/if}
			</p>
		</ModalBody>

		<ModalFooter
			onCancel={() => (confirmDialogOpen = false)}
			primaryAction={{
				label:
					confirmAction === 'remove'
						? 'Remove'
						: confirmAction === 'promote'
							? 'Promote'
							: 'Demote',
				onclick: handleConfirmAction,
				destructive: confirmAction === 'remove'
			}}
		/>
	{/snippet}
</Dialog>

<!-- Edit Member/Phantom Dialog -->
<Dialog bind:open={editDialogOpen}>
	{#snippet children()}
		<ModalHeader title="Edit player" />

		<ModalBody>
			<div class="modal-form">
				<div class="form-fields">
					<div class="form-field">
						<label for="joinDate">Join date</label>
						<input id="joinDate" type="date" bind:value={editJoinDate} class="date-input" />
					</div>
					<p class="help-text">
						This date is used to determine which events a member was active for when adding
						historical GW scores.
					</p>
					<SettingsRow title="Retired" subtitle="This player is no longer a part of the crew">
						{#snippet control()}
							<Switch bind:checked={editRetired} name="retired" />
						{/snippet}
					</SettingsRow>
				</div>
			</div>
		</ModalBody>

		<ModalFooter
			onCancel={() => (editDialogOpen = false)}
			primaryAction={{
				label: 'Save',
				onclick: handleSaveEdit,
				disabled: !editJoinDate
			}}
		/>
	{/snippet}
</Dialog>

<!-- Delete Phantom Confirmation Dialog -->
<Dialog bind:open={deletePhantomDialogOpen}>
	{#snippet children()}
		<ModalHeader title="Delete Phantom Player" />

		<ModalBody>
			<p class="confirm-message">
				Are you sure you want to delete {phantomToDelete?.name ?? 'this phantom player'}? This
				action cannot be undone.
			</p>
		</ModalBody>

		<ModalFooter
			onCancel={() => (deletePhantomDialogOpen = false)}
			primaryAction={{
				label: 'Delete',
				onclick: handleConfirmDeletePhantom,
				destructive: true
			}}
		/>
	{/snippet}
</Dialog>

<!-- Scout User Modal -->
{#if crewStore.crew?.id}
	<ScoutUserModal bind:open={scoutModalOpen} crewId={crewStore.crew.id} />
	<BulkPhantomModal bind:open={bulkPhantomDialogOpen} crewId={crewStore.crew.id} />
	<AssignPhantomModal
		bind:open={assignPhantomDialogOpen}
		crewId={crewStore.crew.id}
		phantom={phantomToAssign}
	/>
	<ConfirmClaimModal
		bind:open={confirmClaimDialogOpen}
		crewId={crewStore.crew.id}
		phantom={phantomToClaim}
	/>
{/if}

<style lang="scss">
	@use '$src/themes/colors' as colors;
	@use '$src/themes/effects' as effects;
	@use '$src/themes/spacing' as spacing;
	@use '$src/themes/typography' as typography;
	@use '$src/themes/layout' as layout;

	.page {
		margin: 0 auto;
		max-width: var(--main-max-width);
	}

	.card {
		background: var(--card-bg);
		border: 0.5px solid rgba(0, 0, 0, 0.18);
		border-radius: layout.$page-corner;
		box-shadow: effects.$page-elevation;
		overflow: hidden;

		:global(.header-info) {
			gap: spacing.$unit-2x;
		}
	}

	.filter-tabs {
		display: flex;
		gap: spacing.$unit-half;
	}

	.filter-tab {
		padding: 4px spacing.$unit;
		background: none;
		border: none;
		border-radius: layout.$item-corner-small;
		color: var(--text-secondary);
		cursor: pointer;
		font-size: typography.$font-small;
		transition:
			color 0.15s,
			background-color 0.15s;

		&:hover {
			color: var(--text-primary);
			background: rgba(0, 0, 0, 0.04);
		}

		&.active {
			color: var(--text-primary);
			background: rgba(0, 0, 0, 0.06);
			font-weight: typography.$medium;
		}
	}

	.loading-state,
	.error-state {
		display: flex;
		justify-content: center;
		align-items: center;
		padding: spacing.$unit-4x;
		color: var(--text-secondary);
		font-size: typography.$font-small;
	}

	.empty-state {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: spacing.$unit-2x;
		text-align: center;
		color: var(--text-secondary);
		padding: spacing.$unit-4x;
		font-size: typography.$font-small;

		p {
			margin: 0;
		}
	}

	.member-list {
		list-style: none;
		margin: 0;
		padding: spacing.$unit;
	}

	.section-divider {
		display: flex;
		align-items: center;
		padding: spacing.$unit spacing.$unit-2x;
		background: rgba(0, 0, 0, 0.02);
		border-bottom: 1px solid rgba(0, 0, 0, 0.06);

		&.pending-claims {
			background: var(--color-yellow-light, #fef9c3);
			border-bottom-color: var(--color-yellow-dark, #854d0e);

			span {
				color: var(--color-yellow-dark, #854d0e);
			}
		}

		span {
			font-size: typography.$font-small;
			font-weight: typography.$medium;
			color: var(--text-secondary);
		}
	}

	// Confirm dialog styles
	.confirm-message {
		color: var(--text-primary);
		line-height: 1.5;
		margin: 0;
	}

	// Modal form styles
	.modal-form {
		display: flex;
		flex-direction: column;
		gap: spacing.$unit-3x;
	}

	.form-fields {
		display: flex;
		flex-direction: column;
		gap: spacing.$unit-3x;
	}

	.form-field {
		display: flex;
		flex-direction: column;
		gap: spacing.$unit-half;

		label {
			font-size: typography.$font-small;
			font-weight: typography.$medium;
			color: var(--text-primary);
		}
	}

	:global(fieldset) {
		border: none;
		padding: 0;
		margin: 0;
	}

	.date-input {
		padding: spacing.$unit spacing.$unit-2x;
		border: none;
		border-radius: layout.$input-corner;
		font-size: typography.$font-regular;
		font-family: inherit;
		background: var(--input-bound-bg);
		color: var(--text-primary);
		width: 100%;

		&:hover {
			background: var(--input-bound-bg-hover);
		}

		&:focus {
			outline: none;
			background: var(--input-bound-bg-hover);
		}
	}

	.help-text {
		font-size: typography.$font-small;
		color: var(--text-secondary);
		margin: 0;
		line-height: 1.4;
	}

	// Invitation row styles
	.invitation-row {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: spacing.$unit spacing.$unit spacing.$unit spacing.$unit-2x;
		border-radius: layout.$item-corner;
		transition: background-color 0.15s;

		&:hover {
			background: rgba(0, 0, 0, 0.03);
		}

		&.expired {
			opacity: 0.5;
		}
	}

	.invitation-info {
		display: flex;
		flex-direction: column;
		gap: 2px;
	}

	.invited-user {
		font-size: typography.$font-small;
		font-weight: typography.$medium;
		color: var(--text-primary);
	}

	.invited-by {
		font-size: typography.$font-tiny;
		color: var(--text-tertiary);
	}

	.invitation-status {
		display: flex;
		align-items: center;
	}

	.expires-text {
		font-size: typography.$font-tiny;
		color: var(--text-tertiary);
	}

	.status-badge.expired {
		font-size: typography.$font-tiny;
		color: colors.$error;
		background: colors.$error--bg--light;
		padding: 2px 8px;
		border-radius: 4px;
		font-weight: typography.$medium;
	}
</style>
