// * Internal directives *

import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy, OnInit } from '@angular/core';
import { ParticipantModel } from '../../models/participant.model';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';

function isVideoconferenceHost(elementRef: ElementRef): boolean {
	return elementRef.nativeElement?.tagName?.toLowerCase() === 'ov-videoconference';
}

/**
 * Load default OpenVidu logo if custom one is not exist
 * @internal
 */
@Directive({
	selector: 'img[ovLogo]',
	standalone: false
})
export class FallbackLogoDirective implements OnInit {
	defaultLogo =
		'data:image/png;base64, ';
	assetsLogo = 'assets/images/logo.png';
	@Input() ovLogo: string;
	constructor(public elementRef: ElementRef) {}

	ngOnInit() {
		this.loadImage(this.ovLogo || this.assetsLogo);
	}

	private loadImage(url: string) {
		const element = this.elementRef.nativeElement as HTMLImageElement;
		const tempImage = new Image();

		const handleImageErrorOrLoad = (url: string) => {
			if (tempImage.width <= 1 && tempImage.height <= 1) {
				if (url === this.ovLogo) {
					this.loadImage(this.assetsLogo);
				} else if (url === this.assetsLogo) {
					element.src = this.defaultLogo;
				}
			} else {
				element.src = url;
			}
		};

		tempImage.onload = () => {
			handleImageErrorOrLoad(url);
		};

		tempImage.onerror = () => {
			handleImageErrorOrLoad(url);
		};
		tempImage.src = url;
	}
}

/**
 * @internal
 */
@Directive({
	selector: 'ov-layout[ovRemoteParticipants]',
	standalone: false
})
export class LayoutRemoteParticipantsDirective {
	private _ovRemoteParticipants: ParticipantModel[] | undefined;

	@Input() set ovRemoteParticipants(value: ParticipantModel[] | undefined) {
		this._ovRemoteParticipants = value;
		this.update(value);
	}
	constructor(
		public elementRef: ElementRef,
		private directiveService: OpenViduComponentsConfigService
	) {}

	ngOnDestroy(): void {
		if (isVideoconferenceHost(this.elementRef)) {
			this.clear();
		}
	}

	ngAfterViewInit() {
		this.update(this._ovRemoteParticipants);
	}

	update(value: ParticipantModel[] | undefined) {
		this.directiveService.setLayoutRemoteParticipants(value);
	}

	clear() {
		this.update(undefined);
	}
}

/**
 * @internal
 */
@Directive({
	selector: 'ov-videoconference[brandingLogo], ov-toolbar[brandingLogo]',
	standalone: false
})
export class ToolbarBrandingLogoDirective implements AfterViewInit, OnDestroy {
	/**
	 * @ignore
	 */
	@Input() set brandingLogo(value: string) {
		this._brandingLogo = value;
		this.update(this._brandingLogo);
	}

	private _brandingLogo: string = '';

	/**
	 * @ignore
	 */
	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.update(this._brandingLogo);
	}

	ngOnDestroy(): void {
		if (isVideoconferenceHost(this.elementRef)) {
			this.clear();
		}
	}
	private clear() {
		this._brandingLogo = '';
		this.update(this._brandingLogo);
	}

	private update(value: string) {
		this.libService.updateToolbarConfig({ brandingLogo: value });
	}
}

/**
 * @internal
 */
@Directive({
	selector: 'ov-videoconference[prejoinDisplayParticipantName]',
	standalone: false
})
export class PrejoinDisplayParticipantName implements OnDestroy {
	/**
	 * @ignore
	 */
	@Input() set prejoinDisplayParticipantName(value: boolean) {
		this.update(value);
	}

	/**
	 * @ignore
	 */
	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngOnDestroy(): void {
		if (isVideoconferenceHost(this.elementRef)) {
			this.clear();
		}
	}

	private clear() {
		this.update(true);
	}

	private update(value: boolean) {
		this.libService.updateGeneralConfig({ prejoinDisplayParticipantName: value });
	}
}

/**
 * @internal
 *
 * The **recordingActivityReadOnly** directive sets the recording activity panel to read-only mode.
 * In this mode, users can only view recordings without the ability to start, stop, or delete them.
 *
 * It is only available for {@link VideoconferenceComponent}.
 *
 * Default: `false`
 *
 * @example
 * <ov-videoconference [recordingActivityReadOnly]="true"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[recordingActivityReadOnly]',
	standalone: false
})
export class RecordingActivityReadOnlyDirective implements OnDestroy {
	/**
	 * @ignore
	 */
	@Input() set recordingActivityReadOnly(value: boolean) {
		this.update(value);
	}

	/**
	 * @ignore
	 */
	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	/**
	 * @ignore
	 */
	ngOnDestroy(): void {
		this.clear();
	}

	/**
	 * @ignore
	 */
	clear() {
		this.update(false);
	}

	/**
	 * @ignore
	 */
	update(value: boolean) {
		this.libService.updateRecordingActivityConfig({ readOnly: value });
	}
}

/**
 *
 * @internal
 *
 * The **recordingActivityShowControls** directive allows to show/hide specific recording controls (play, download, delete, externalView).
 * You can pass an object with boolean properties to control which buttons are shown.
 *
 * It is only available for {@link VideoconferenceComponent}.
 *
 * Default: `{ play: true, download: true, delete: true, externalView: false }`
 *
 * @example
 * <ov-videoconference [recordingActivityShowControls]="{ play: false, download: true, delete: false }"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[recordingActivityShowControls]',
	standalone: false
})
export class RecordingActivityShowControlsDirective implements OnDestroy {
	/**
	 * @ignore
	 */
	@Input() set recordingActivityShowControls(value: { play: boolean; download: boolean; delete: boolean; externalView: boolean }) {
		this.update(value);
	}

	/**
	 * @ignore
	 */
	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	/**
	 * @ignore
	 */
	ngOnDestroy(): void {
		this.clear();
	}

	/**
	 * @ignore
	 */
	clear() {
		this.update({ play: true, download: true, delete: true, externalView: false });
	}

	/**
	 * @ignore
	 */
	update(value: { play: boolean; download: boolean; delete: boolean; externalView: boolean }) {
		this.libService.updateRecordingActivityConfig({ showControls: value });
	}
}

/**
 * @internal
 * The **viewRecordingsButton** directive allows show/hide the view recordings toolbar button.
 *
 * Default: `false`
 *
 * It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
 *
 * @example
 * <ov-videoconference [toolbarViewRecordingsButton]="true"></ov-videoconference>
 *
 * \
 * And it also can be used in the {@link ToolbarComponent}.
 * @example
 * <ov-toolbar [viewRecordingsButton]="true"></ov-toolbar>
 *
 * When the button is clicked, it will fire the `onViewRecordingsClicked` event.
 */
@Directive({
	selector: 'ov-videoconference[toolbarViewRecordingsButton], ov-toolbar[viewRecordingsButton]',
	standalone: false
})
export class ToolbarViewRecordingsButtonDirective implements AfterViewInit, OnDestroy {
	/**
	 * @ignore
	 */
	@Input() set toolbarViewRecordingsButton(value: boolean) {
		this.viewRecordingsValue = value;
		this.update(this.viewRecordingsValue);
	}
	/**
	 * @ignore
	 */
	@Input() set viewRecordingsButton(value: boolean) {
		this.viewRecordingsValue = value;
		this.update(this.viewRecordingsValue);
	}

	private viewRecordingsValue: boolean = false;

	/**
	 * @ignore
	 */
	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.update(this.viewRecordingsValue);
	}

	ngOnDestroy(): void {
		this.clear();
	}
	private clear() {
		this.viewRecordingsValue = false;
		this.update(this.viewRecordingsValue);
	}

	private update(value: boolean) {
		this.libService.updateToolbarConfig({ viewRecordings: value });
	}
}

/**
 * @internal
 *
 * The **recordingActivityStartStopRecordingButton** directive allows to show or hide the start/stop recording buttons in recording activity.
 *
 * Default: `true`
 *
 * It is only available for {@link VideoconferenceComponent}.
 *
 * @example
 * <ov-videoconference [recordingActivityStartStopRecordingButton]="false"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[recordingActivityStartStopRecordingButton]',
	standalone: false
})
export class StartStopRecordingButtonsDirective implements OnDestroy {
	/**
	 * @ignore
	 */
	@Input() set recordingActivityStartStopRecordingButton(value: boolean) {
		this.update(value);
	}

	/**
	 * @ignore
	 */
	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngOnDestroy(): void {
		this.clear();
	}

	private clear() {
		this.update(true);
	}

	private update(value: boolean) {
		this.libService.updateRecordingActivityConfig({ startStopButton: value });
	}
}

/**
 * @internal
 * The **recordingActivityViewRecordingsButton** directive allows to show/hide the view recordings button in the recording activity panel.
 *
 * Default: `false`
 *
 * Can be used in {@link VideoconferenceComponent}.
 *
 * @example
 * <ov-videoconference [recordingActivityViewRecordingsButton]="true"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[recordingActivityViewRecordingsButton]',
	standalone: false
})
export class RecordingActivityViewRecordingsButtonDirective implements AfterViewInit, OnDestroy {
	@Input() set recordingActivityViewRecordingsButton(value: boolean) {
		this._value = value;
		this.update(this._value);
	}

	private _value: boolean = false;

	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.update(this._value);
	}

	ngOnDestroy(): void {
		this.clear();
	}

	private clear() {
		this._value = false;
		this.update(this._value);
	}

	private update(value: boolean) {
		this.libService.updateRecordingActivityConfig({ viewRecordingsButton: value });
	}
}

/**
 * @internal
 * The **recordingActivityShowRecordingsList** directive allows to show or hide the recordings list in the recording activity panel.
 *
 * Default: `true`
 *
 * Can be used in {@link VideoconferenceComponent}.
 *
 * @example
 * <ov-videoconference [recordingActivityShowRecordingsList]="false"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[recordingActivityShowRecordingsList]',
	standalone: false
})
export class RecordingActivityShowRecordingsListDirective implements AfterViewInit, OnDestroy {
	@Input() set recordingActivityShowRecordingsList(value: boolean) {
		this._value = value;
		this.update(this._value);
	}

	private _value: boolean = true;

	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.update(this._value);
	}

	ngOnDestroy(): void {
		this.clear();
	}

	private clear() {
		this._value = false;
		this.update(this._value);
	}

	private update(value: boolean) {
		this.libService.updateRecordingActivityConfig({ showRecordingsList: value });
	}
}

/**
 * @internal
 * The **toolbarRoomName** directive allows to display a specific room name in the toolbar.
 * If the room name is not set, it will display the room ID instead.
 *
 * Can be used in {@link ToolbarComponent}.
 *
 * @example
 * <ov-videoconference [toolbarRoomName]="roomName"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[toolbarRoomName], ov-toolbar[roomName]',
	standalone: false
})
export class ToolbarRoomNameDirective implements AfterViewInit, OnDestroy {
	@Input() set toolbarRoomName(value: string | undefined) {
		this._roomName = value;
		this.updateRoomName();
	}

	@Input() set roomName(value: string | undefined) {
		this._roomName = value;
		this.updateRoomName();
	}

	private _roomName?: string;

	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.updateRoomName();
	}

	ngOnDestroy(): void {
		this.clear();
	}

	private clear() {
		this._roomName = undefined;
		this.updateRoomName();
	}

	private updateRoomName() {
		this.libService.updateToolbarConfig({ roomName: this._roomName || '' });
	}
}

/**
 * @internal
 *
 * The **showThemeSelector** directive allows to enable or disable the theme selector control.
 * When disabled, users won't be able to change the UI theme.
 *
 * Default: `false`
 *
 * Usage:
 * <ov-videoconference [showThemeSelector]="false"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[showThemeSelector]',
	standalone: false
})
export class ShowThemeSelectorDirective implements AfterViewInit, OnDestroy {
	@Input() set showThemeSelector(value: boolean) {
		this._value = value;
		this.update(this._value);
	}

	private _value: boolean = false;

	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.update(this._value);
	}

	ngOnDestroy(): void {
		this.clear();
	}

	private clear() {
		this._value = true;
		this.update(this._value);
	}

	private update(value: boolean) {
		this.libService.updateGeneralConfig({ showThemeSelector: value });
	}
}

/**
 * @internal
 *
 * The **e2eeKey** directive allows to configure end-to-end encryption for the videoconference.
 * When provided, the room will be configured with E2EE using an external key provider.
 *
 * Default: `undefined`
 *
 * Usage:
 * <ov-videoconference [e2eeKey]="yourEncryptionKey"></ov-videoconference>
 */
@Directive({
	selector: 'ov-videoconference[e2eeKey]',
	standalone: false
})
export class E2EEKeyDirective implements AfterViewInit, OnDestroy {
	@Input() set e2eeKey(value: string | undefined) {
		this._value = value;
		this.update(this._value);
	}

	private _value: string | undefined;

	constructor(
		public elementRef: ElementRef,
		private libService: OpenViduComponentsConfigService
	) {}

	ngAfterViewInit() {
		this.update(this._value);
	}

	ngOnDestroy(): void {
		this.clear();
	}

	private clear() {
		this._value = undefined;
		this.update(this._value);
	}

	private update(value: string | undefined) {
		// Only update if value is valid (not undefined, not null, not empty string)
		const validValue = value && value.trim() !== '' ? value.trim() : undefined;
		this.libService.updateGeneralConfig({ e2eeKey: validValue });
	}
}
