import SwiftUI
import SFSafeSymbols

/// Saved devices screen. A native `List` of saved devices and outstanding consent
/// requests; selecting one opens the per-device details surface. The global
/// targeted-transfer history is deliberately absent — transfers belong to a
/// device, and are reachable only through it.
struct SavedDevicesScreen: View {
	@ObservedObject var model: SavedDevicesModel
	let windowClass: WindowClass

	/// Endpoint ID of the device whose details are open.
	@State private var selectedPeerId: String?

	private var state: SavedDevicesState { model.state }

	var body: some View {
		NavigationStack {
			Group {
				if state.isLoading && state.isEmpty {
					loadingState
				} else if state.loadFailed && state.isEmpty {
					failedState
				} else if state.isEmpty {
					emptyState
				} else {
					deviceList
				}
			}
			// Title-only header once populated: explanatory copy belongs in the
			// first-use empty state, not above a list the user already understands.
			.navigationTitle(Text(String(localized: L10n.Saved.devicesListTitle)))
		}
		// The consent prompts are hosted at the app root, not here: they must be
		// answerable from any tab, not only while this screen is showing.
		.savedDeviceDetails(model: model, windowClass: windowClass, selectedPeerId: $selectedPeerId)
	}

	// MARK: - List

	private var deviceList: some View {
		List {
			if !state.eligibilities.isEmpty || !state.pendingRelationships.isEmpty {
				Section {
					ForEach(state.pendingRelationships) { relationship in
						PendingRelationshipRow(
							relationship: relationship,
							busy: state.busyPeerIds.contains(relationship.remoteEndpointId),
							onAccept: { model.acceptIncoming(relationship.remoteEndpointId) },
							onDecline: { model.declineIncoming(relationship.remoteEndpointId) }
						)
					}
					ForEach(pendingEligibilities) { eligibility in
						EligibilityRow(
							eligibility: eligibility,
							busy: state.busyPeerIds.contains(eligibility.peerEndpointId),
							onRemember: { model.rememberEligible(eligibility.peerEndpointId) },
							onDecline: { model.declineEligible(eligibility.peerEndpointId) }
						)
					}
				} header: {
					Text(String(localized: L10n.Saved.devicesAttentionTitle))
				}
			}

			if !state.savedDevices.isEmpty {
				Section {
					ForEach(state.savedDevices) { device in
						Button { selectedPeerId = device.endpointId } label: {
							SavedDeviceRow(
								device: device,
								transferCount: state.transfers(for: device.endpointId).count,
								busy: state.busyPeerIds.contains(device.endpointId)
							)
						}
						.buttonStyle(.plain)
						.contextMenu { deviceMenu(device) }
					}
				} header: {
					Text(String(localized: L10n.Saved.devicesPendingTitle))
				}
			}
		}
	}

	/// Eligibilities whose peer already has a pending relationship are represented
	/// by that relationship row instead, so the same device never appears twice.
	private var pendingEligibilities: [PairingEligibilityModel] {
		let pendingIds = Set(state.pendingRelationships.map(\.remoteEndpointId))
		return state.eligibilities.filter { !pendingIds.contains($0.peerEndpointId) }
	}

	@ViewBuilder
	private func deviceMenu(_ device: SavedDeviceModel) -> some View {
		Button {
			selectedPeerId = device.endpointId
		} label: {
			Label(String(localized: L10n.Saved.devicesSendAction), systemSymbol: .paperplane)
		}
		Button {
			selectedPeerId = device.endpointId
			model.openLabelEditor(device.endpointId)
		} label: {
			Label(String(localized: L10n.Saved.devicesLabelAction), systemSymbol: .pencil)
		}
	}

	// MARK: - Placeholder states

	private var loadingState: some View {
		VStack(spacing: 12) {
			ProgressView()
			Text(String(localized: L10n.Saved.devicesLoading))
				.font(.subheadline)
				.foregroundStyle(.secondary)
		}
		.frame(maxWidth: .infinity, maxHeight: .infinity)
	}

	private var failedState: some View {
		ContentUnavailableView {
			Label(String(localized: L10n.Saved.devicesLoadFailed), systemSymbol: .exclamationmarkTriangleFill)
		} actions: {
			Button(String(localized: L10n.Button.retry), action: model.retry)
				.buttonStyle(.borderedProminent)
		}
	}

	/// First-use state: the only place that explains what a saved device is.
	private var emptyState: some View {
		ContentUnavailableView {
			Label(String(localized: L10n.Saved.devicesEmptyTitle), systemSymbol: .macbookAndIphone)
		} description: {
			Text(String(localized: L10n.Saved.devicesEmpty))
		}
	}
}

// MARK: - Rows

private struct SavedDeviceRow: View {
	let device: SavedDeviceModel
	let transferCount: Int
	let busy: Bool

	var body: some View {
		HStack(spacing: 12) {
			DeviceAvatar()
			VStack(alignment: .leading, spacing: 3) {
				Text(device.displayName)
					.font(.body)
					.lineLimit(1)
					.truncationMode(.tail)
				EndpointIdLabel(endpointId: device.endpointId)
			}
			Spacer(minLength: 8)
			if busy {
				ProgressView().controlSize(.small)
			} else if transferCount > 0 {
				Text(verbatim: "\(transferCount)")
					.font(.caption)
					.foregroundStyle(.secondary)
					.monospacedDigit()
			}
			Image(systemSymbol: .chevronRight)
				.font(.caption)
				.foregroundStyle(.tertiary)
		}
		.contentShape(Rectangle())
	}
}

/// A relationship awaiting consent. Incoming requests get accept/decline; an
/// outgoing request is informational until the peer answers.
private struct PendingRelationshipRow: View {
	let relationship: DeviceRelationshipModel
	let busy: Bool
	let onAccept: () -> Void
	let onDecline: () -> Void

	private var isIncoming: Bool { relationship.state == .pendingIncoming }

	var body: some View {
		VStack(alignment: .leading, spacing: 10) {
			HStack(spacing: 12) {
				DeviceAvatar(symbol: .personBadgeClock, tint: .orange)
				VStack(alignment: .leading, spacing: 3) {
					Text(String(localized: isIncoming
						? L10n.Saved.devicesPendingIncoming
						: L10n.Saved.devicesPendingOutgoing))
						.font(.body)
						.lineLimit(2)
					EndpointIdLabel(endpointId: relationship.remoteEndpointId)
				}
				Spacer(minLength: 0)
				if busy { ProgressView().controlSize(.small) }
			}
			if isIncoming {
				HStack(spacing: 10) {
					Button(String(localized: L10n.Saved.devicesAcceptPairingAction), action: onAccept)
						.buttonStyle(.borderedProminent)
					Button(String(localized: L10n.Saved.devicesDeclineAction), role: .destructive, action: onDecline)
						.buttonStyle(.bordered)
				}
				.controlSize(.small)
				.disabled(busy)
			}
		}
		.padding(.vertical, 4)
	}
}

/// A peer we completed a transfer with and may ask to pair. Naming it uses the
/// peer's untrusted hint — the only name available before the device is saved.
private struct EligibilityRow: View {
	let eligibility: PairingEligibilityModel
	let busy: Bool
	let onRemember: () -> Void
	let onDecline: () -> Void

	var body: some View {
		VStack(alignment: .leading, spacing: 10) {
			HStack(spacing: 12) {
				DeviceAvatar(symbol: .checkmarkSealFill, tint: VniDropColors.brandPurple)
				VStack(alignment: .leading, spacing: 3) {
					Text(eligibility.remoteDisplayName ?? String(localized: L10n.Saved.devicesUnnamed))
						.font(.body)
						.lineLimit(1)
					Text(String(localized: L10n.Saved.devicesEligibilityTitle))
						.font(.caption)
						.foregroundStyle(.secondary)
					EndpointIdLabel(endpointId: eligibility.peerEndpointId)
				}
				Spacer(minLength: 0)
				if busy { ProgressView().controlSize(.small) }
			}
			HStack(spacing: 10) {
				Button(String(localized: L10n.Saved.devicesRememberAction), action: onRemember)
					.buttonStyle(.borderedProminent)
				Button(String(localized: L10n.Saved.devicesDeclineAction), role: .destructive, action: onDecline)
					.buttonStyle(.bordered)
			}
			.controlSize(.small)
			.disabled(busy)
		}
		.padding(.vertical, 4)
	}
}
