<template>
	<NcDialog
		:name="resolvedTitle"
		:size="size"
		:no-close="loading"
		@closing="$emit('close')">
		<!-- Result phase (standard mode, not create-another) -->
		<div v-if="result !== null && !createAnother"
			class="cn-tabbed-form-dialog__result"
			data-testid="cn-modal"
			data-testid-modal="cn-tabbed-form-dialog"
			data-testid-phase="result">
			<NcNoteCard v-if="result.success" type="success">
				{{ resolvedSuccessText }}
			</NcNoteCard>
			<NcNoteCard v-if="result.error" type="error">
				{{ result.error }}
			</NcNoteCard>
		</div>

		<!-- Form phase (or create-another mode where form stays visible) -->
		<div v-if="createAnother || result === null"
			class="cn-tabbed-form-dialog__form"
			data-testid="cn-modal"
			data-testid-modal="cn-tabbed-form-dialog"
			data-testid-phase="form">
			<!-- Inline notifications for create-another mode -->
			<NcNoteCard v-if="createAnother && result && result.success" type="success">
				{{ resolvedSuccessText }}
			</NcNoteCard>
			<NcNoteCard v-if="result && result.error" type="error">
				{{ result.error }}
			</NcNoteCard>

			<!-- Optional content above tabs (e.g. metadata grid, detail cards) -->
			<slot name="above-tabs" :loading="loading" />

			<!-- Tabs — hand-rolled to avoid the bootstrap-vue → bootstrap@4 → jquery
			     missing-peer chain in consumer SBOM. UX parity with BTabs/BTab:
			     justified tabs, icon + title, disabled state, content panel below. -->
			<div class="cn-tabbed-form-dialog__tabs tabContainer">
				<ul class="cn-tabbed-form-dialog__tab-nav" role="tablist">
					<li
						v-for="(tab, idx) in tabs"
						:key="tab.id"
						role="presentation"
						class="cn-tabbed-form-dialog__tab-nav-item">
						<button
							type="button"
							role="tab"
							:aria-selected="activeTab === idx"
							:aria-controls="'cn-tab-panel-' + tab.id"
							:disabled="tab.disabled"
							:class="['cn-tabbed-form-dialog__tab-button', { 'is-active': activeTab === idx }]"
							@click="onTabClick(idx)">
							<component :is="tab.icon" v-if="tab.icon" :size="16" />
							<span>{{ tab.title }}</span>
						</button>
					</li>
				</ul>
				<div
					v-for="(tab, idx) in tabs"
					v-show="activeTab === idx"
					:id="'cn-tab-panel-' + tab.id"
					:key="tab.id"
					role="tabpanel"
					class="cn-tabbed-form-dialog__tab-content form-editor">
					<slot :name="'tab-' + tab.id" :loading="loading" />
				</div>
			</div>

			<!-- Optional content below tabs (e.g. shared settings across all tabs) -->
			<slot name="below-tabs" :loading="loading" />
		</div>

		<template #actions>
			<!-- Create another checkbox (only in create mode) -->
			<NcCheckboxRadioSwitch
				v-if="showCreateAnother && isCreateMode"
				v-model="createAnother"
				class="cn-tabbed-form-dialog__create-another"
				:disabled="loading">
				{{ createAnotherLabel }}
			</NcCheckboxRadioSwitch>

			<!-- Extra actions before Cancel -->
			<slot name="actions-left"
				:loading="loading"
				:is-create-mode="isCreateMode"
				:result="result" />

			<!-- Cancel / Close button -->
			<NcButton @click="handleClose">
				<template #icon>
					<Cancel :size="20" />
				</template>
				{{ result !== null && !createAnother ? closeLabel : cancelLabel }}
			</NcButton>

			<!-- Extra actions after primary -->
			<slot name="actions-right"
				:loading="loading"
				:is-create-mode="isCreateMode"
				:result="result" />

			<!-- Primary action button (Save / Create) -->
			<NcButton
				v-if="createAnother || result === null"
				:title="disableSave && disableSaveTooltip ? disableSaveTooltip : undefined"
				variant="primary"
				:disabled="loading || disableSave"
				:aria-label="disableSave && disableSaveTooltip ? disableSaveTooltip : undefined"
				@click="executeConfirm">
				<template #icon>
					<NcLoadingIcon v-if="loading" :size="20" />
					<slot v-else-if="$slots['confirm-icon']" name="confirm-icon" />
					<Plus v-else-if="isCreateMode" :size="20" />
					<ContentSaveOutline v-else :size="20" />
				</template>
				{{ resolvedConfirmLabel }}
			</NcButton>
		</template>
	</NcDialog>
</template>

<script>
import { translate as t } from '@nextcloud/l10n'
import {
	NcButton,
	NcDialog,
	NcLoadingIcon,
	NcNoteCard,
	NcCheckboxRadioSwitch,
} from '@nextcloud/vue'

import Cancel from 'vue-material-design-icons/Cancel.vue'
import ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'
import Plus from 'vue-material-design-icons/Plus.vue'

export default {
	name: 'CnTabbedFormDialog',
	components: {
		NcDialog,
		NcButton,
		NcLoadingIcon,
		NcNoteCard,
		NcCheckboxRadioSwitch,
		Cancel,
		ContentSaveOutline,
		Plus,
	},
	props: {
		/**
		 * Array of tab definitions. Each tab must have at least an `id` and `title`.
		 * The optional `icon` field should be a Vue component reference (e.g. an imported MDI icon).
		 * The optional `disabled` field prevents tab selection.
		 * @type {Array<{ id: string, title: string, icon: object, disabled: boolean }>}
		 */
		tabs: {
			type: Array,
			required: true,
			validator: (tabs) => tabs.length > 0 && tabs.every(t => t.id && t.title),
		},
		/**
		 * Existing item for edit mode. Pass null or undefined for plain create
		 * mode. A non-null object without an `id` is also treated as create mode
		 * — useful for pre-filled payloads (extend, clone, duplicate flows).
		 *
		 * @type {object|null}
		 */
		item: {
			type: Object,
			default: null,
		},
		/**
		 * Custom dialog title. When provided, overrides the auto-generated
		 * "Create {entityName}" / "Edit {entityName}" title.
		 *
		 * @type {string}
		 */
		dialogTitle: {
			type: String,
			default: '',
		},
		/**
		 * Entity name used in auto-generated titles and success messages.
		 * For example, "Organisation" produces "Create Organisation" and
		 * "Organisation saved successfully".
		 *
		 * @type {string}
		 */
		entityName: {
			type: String,
			default: () => t('nextcloud-vue', 'Item'),
		},
		/**
		 * NcDialog size. One of 'small', 'normal', 'large', 'full'.
		 *
		 * @type {string}
		 */
		size: {
			type: String,
			default: 'large',
		},
		/**
		 * Whether to show the "Create Another" checkbox in create mode.
		 * When checked and a save succeeds, the form stays open and a reset
		 * event is emitted so the parent can clear form data.
		 *
		 * @type {boolean}
		 */
		showCreateAnother: {
			type: Boolean,
			default: false,
		},
		/**
		 * Whether the primary save/create button is disabled.
		 * The parent controls validation externally.
		 *
		 * @type {boolean}
		 */
		disableSave: {
			type: Boolean,
			default: false,
		},
		/**
		 * Tooltip shown on the save button when it is disabled.
		 * Also used as aria-label so screen readers can explain the blocked state (WCAG 2.1 AA).
		 *
		 * @type {string}
		 */
		disableSaveTooltip: {
			type: String,
			default: '',
		},
		/**
		 * Custom success message shown in the result NcNoteCard.
		 * Defaults to "{entityName} saved successfully".
		 *
		 * @type {string}
		 */
		successText: {
			type: String,
			default: '',
		},
		/**
		 * Cancel button label.
		 *
		 * @type {string}
		 */
		cancelLabel: {
			type: String,
			default: () => t('nextcloud-vue', 'Cancel'),
		},
		/**
		 * Close button label shown in the result phase.
		 *
		 * @type {string}
		 */
		closeLabel: {
			type: String,
			default: () => t('nextcloud-vue', 'Close'),
		},
		/**
		 * Primary confirm button label. Defaults to "Create" in create mode
		 * or "Save" in edit mode.
		 *
		 * @type {string}
		 */
		confirmLabel: {
			type: String,
			default: '',
		},
		/**
		 * Label for the "Create Another" checkbox.
		 *
		 * @type {string}
		 */
		createAnotherLabel: {
			type: String,
			default: () => t('nextcloud-vue', 'Create another'),
		},
	},
	emits: ['close', 'confirm', 'reset', 'update:activeTab'],
	data() {
		return {
			/** @type {number} Current active tab index */
			activeTab: 0,
			/** @type {boolean} Whether the "create another" checkbox is checked */
			createAnother: false,
			/** @type {boolean} Whether an API operation is in progress */
			loading: false,
			/**
			 * Result of the last operation.
			 * null = form phase, { success: true } = success, { error: 'msg' } = error
			 * @type {{ success: boolean, error: string }|null}
			 */
			result: null,
			/** @type {number|null} Timeout ID for auto-close after success */
			closeTimeout: null,
			/** @type {number|null} Timeout ID for clearing success in create-another mode */
			successClearTimeout: null,
		}
	},
	computed: {
		/**
		 * Whether the dialog is in create mode (no existing persisted item).
		 *
		 * An item is considered "existing" only when it has an `id`. A non-null
		 * item without an `id` (e.g. a pre-filled payload for extend/clone flows)
		 * is treated as create mode.
		 *
		 * @return {boolean}
		 */
		isCreateMode() {
			return !this.item || !this.item.id
		},
		/**
		 * Resolved dialog title. Uses dialogTitle prop if provided,
		 * otherwise auto-generates from entityName and mode.
		 *
		 * @return {string}
		 */
		resolvedTitle() {
			if (this.dialogTitle) {
				return this.dialogTitle
			}
			return this.isCreateMode
				? t('nextcloud-vue', 'Create {title}', { title: this.entityName })
				: t('nextcloud-vue', 'Edit {title}', { title: this.entityName })
		},
		/**
		 * Resolved success text for NcNoteCard.
		 *
		 * @return {string}
		 */
		resolvedSuccessText() {
			if (this.successText) {
				return this.successText
			}
			return t('nextcloud-vue', '{title} saved successfully.', { title: this.entityName })
		},
		/**
		 * Resolved primary button label.
		 *
		 * @return {string}
		 */
		resolvedConfirmLabel() {
			if (this.confirmLabel) {
				return this.confirmLabel
			}
			return this.isCreateMode ? t('nextcloud-vue', 'Create') : t('nextcloud-vue', 'Save')
		},
	},
	beforeUnmount() {
		clearTimeout(this.closeTimeout)
		clearTimeout(this.successClearTimeout)
	},
	methods: {
		/**
		 * Switch to a tab by index, mirroring the BTabs `v-model` + `@input`
		 * contract so existing parent listeners keep working.
		 *
		 * @param {number} idx Tab index to activate.
		 */
		onTabClick(idx) {
			if (this.activeTab === idx) return
			this.activeTab = idx
			this.$emit('update:activeTab', idx)
		},

		/**
		 * Set the result of the save operation. Call this from the parent
		 * after the API call completes.
		 *
		 * When success is true and create-another is not checked, the dialog
		 * auto-closes after 2 seconds. When create-another is checked,
		 * the success message shows inline for 2 seconds, then clears and
		 * emits the `reset` event.
		 *
		 * @param {{ success?: boolean, error?: string }} resultData - Result data to pass to the dialog
		 * @public
		 */
		setResult(resultData) {
			this.loading = false
			this.result = resultData

			if (resultData.success) {
				if (this.createAnother) {
					// Create-another mode: show success briefly, then reset
					this.successClearTimeout = setTimeout(() => {
						this.result = null
						this.activeTab = 0
						/**
						 * Emitted after a successful save in create-another mode.
						 * The parent should clear its form data.
						 *
						 * @event reset
						 */
						this.$emit('reset')
					}, 2000)
				} else {
					// Standard mode: auto-close after 2 seconds
					this.closeTimeout = setTimeout(() => {
						/**
						 * Emitted when the dialog should be closed.
						 *
						 * @event close
						 */
						this.$emit('close')
					}, 2000)
				}
			}
		},

		/**
		 * Reset the dialog to its initial form state.
		 * Clears any result, resets loading, and returns to the first tab.
		 *
		 * @public
		 */
		resetDialog() {
			clearTimeout(this.closeTimeout)
			clearTimeout(this.successClearTimeout)
			this.result = null
			this.loading = false
			this.activeTab = 0
		},

		/**
		 * Handle the primary action button click.
		 * Sets loading state and emits the confirm event.
		 *
		 * @private
		 */
		executeConfirm() {
			this.loading = true
			this.result = null
			/**
			 * Emitted when the user clicks Save/Create.
			 * The parent should perform the API call and then call setResult().
			 *
			 * @event confirm
			 */
			this.$emit('confirm')
		},

		/**
		 * Handle close button click. Clears timeouts and emits close.
		 *
		 * @private
		 */
		handleClose() {
			clearTimeout(this.closeTimeout)
			clearTimeout(this.successClearTimeout)
			this.result = null
			this.loading = false
			this.createAnother = false
			this.activeTab = 0
			this.$emit('close')
		},
	},
}
</script>

<style scoped>
/* Result phase container */
.cn-tabbed-form-dialog__result {
	padding: 16px 0;
}

/* Form phase container */
.cn-tabbed-form-dialog__form {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* Tabs wrapper */
.cn-tabbed-form-dialog__tabs {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

/* Tab content area — also uses .form-editor for compatibility */
.cn-tabbed-form-dialog__tab-content {
	display: flex;
	flex-direction: column;
	gap: 16px;
	padding: 16px 0;
}

/* Create another checkbox — push to the left in actions area */
.cn-tabbed-form-dialog__create-another {
	margin-right: auto;
}

/* Bootstrap-Vue tab container styling */
.tabContainer > * ul > li {
	display: flex;
	flex: 1;
}

.tabContainer > * ul > li:hover {
	background-color: var(--color-background-hover);
}

.tabContainer > * ul > li > a {
	flex: 1;
	text-align: center;
}

.tabContainer > * ul > li > .active {
	background: transparent !important;
	color: var(--color-main-text) !important;
	border-bottom: var(--default-grid-baseline) solid var(--color-primary-element) !important;
}

.tabContainer > * ul[role="tablist"] {
	display: flex;
	margin: 10px 8px 0 8px;
	justify-content: space-between;
	border-bottom: 1px solid var(--color-border);
}

.tabContainer > * ul[role="tablist"] > * a[role="tab"] {
	padding-inline-start: 10px;
	padding-inline-end: 10px;
	padding-block-start: 10px;
	padding-block-end: 10px;
}

.tabContainer > * div[role="tabpanel"] {
	margin-block-start: var(--OR-margin-10);
}

:deep(.nav-tabs) {
	border-bottom: 1px solid var(--color-border);
	margin-bottom: 15px;
	display: flex;
}

:deep(.nav-tabs .nav-item) {
	display: flex;
	flex: 1;
}

:deep(.nav-tabs .nav-link) {
	flex: 1;
	text-align: center;
	border: none;
	border-bottom: 2px solid transparent;
	color: var(--color-text-maxcontrast);
	padding: 8px 16px;
	display: flex;
	align-items: center;
	gap: 8px;
	justify-content: center;
}

:deep(.nav-tabs .nav-link.active) {
	color: var(--color-main-text);
	border-bottom: 2px solid var(--color-primary);
	background-color: transparent;
}

:deep(.nav-tabs .nav-link:hover) {
	border-bottom: 2px solid var(--color-border);
}

:deep(.nav-tabs .nav-link.disabled) {
	cursor: not-allowed;
	opacity: 0.5;
	color: var(--color-text-maxcontrast);
	pointer-events: auto;
}
:deep(.nav-tabs .nav-link.disabled *) {
    cursor: not-allowed;
}

:deep(.nav-tabs .nav-link.disabled:hover) {
	border-bottom: 2px solid transparent;
}

:deep(.tab-content) {
	padding: 16px;
	background-color: var(--color-main-background);
}

/* Hand-rolled tab nav — replaces the bootstrap-vue BTabs/BTab pair to
   eliminate the bootstrap-vue → bootstrap@4 → jquery missing-peer chain
   in consumer SBOM. Visual parity with the legacy .nav-tabs above. */
.cn-tabbed-form-dialog__tab-nav {
	display: flex;
	gap: 0;
	list-style: none;
	padding: 0;
	margin: 0;
	border-bottom: 1px solid var(--color-border);
}

.cn-tabbed-form-dialog__tab-nav-item {
	flex: 1 1 0;
}

.cn-tabbed-form-dialog__tab-button {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	width: 100%;
	padding: 10px 14px;
	background: transparent;
	border: 0;
	border-bottom: 2px solid transparent;
	color: var(--color-text-maxcontrast);
	font: inherit;
	cursor: pointer;
}

.cn-tabbed-form-dialog__tab-button:hover:not(:disabled) {
	border-bottom-color: var(--color-border);
	color: var(--color-main-text);
}

.cn-tabbed-form-dialog__tab-button.is-active {
	color: var(--color-main-text);
	border-bottom-color: var(--color-primary);
}

/* stylelint-disable-next-line no-descending-specificity */
.cn-tabbed-form-dialog__tab-button:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}
</style>
