import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'
import { Injectable, inject } from '@angular/core'
import { AnyObject, AnyType } from '@core/types'
import { fromPairs } from 'es-toolkit/compat'
import { map, Observable, ReplaySubject } from 'rxjs'

const screens = {
	sm: '640px',
	md: '768px',
	lg: '1024px',
	xl: '1280px',
}

@Injectable({
	providedIn: 'root',
})
export class FuseMediaWatcherService {
	private _breakpointObserver = inject(BreakpointObserver)

	private _onMediaChange: ReplaySubject<{ matchingAliases: string[]; matchingQueries: AnyType }> = new ReplaySubject<{
		matchingAliases: string[]
		matchingQueries: AnyType
	}>(1)

	constructor() {
		const pairs = fromPairs(Object.entries(screens).map(([alias, screen]) => [alias, `(min-width: ${screen})`]))

		this._breakpointObserver
			.observe(Object.values(pairs))
			.pipe(
				map((state) => {
					// Prepare the observable values and set their defaults
					const matchingAliases: string[] = []
					const matchingQueries: AnyObject = {}

					// Get the matching breakpoints and use them to fill the subject
					const matchingBreakpoints = Object.entries(state.breakpoints).filter(([, matches]) => matches) ?? []
					for (const [query] of matchingBreakpoints) {
						// Find the alias of the matching query
						const matchingAlias = Object.entries(pairs).find(([, q]) => q === query)?.[0]

						// Add the matching query to the observable values
						if (matchingAlias) {
							matchingAliases.push(matchingAlias)
							matchingQueries[matchingAlias] = query
						}
					}

					// Execute the observable
					this._onMediaChange.next({
						matchingAliases,
						matchingQueries,
					})
				}),
			)
			.subscribe()
	}

	get onMediaChange$(): Observable<{ matchingAliases: string[]; matchingQueries: AnyObject }> {
		return this._onMediaChange.asObservable()
	}

	onMediaQueryChange$(query: string | string[]): Observable<BreakpointState> {
		return this._breakpointObserver.observe(query)
	}
}
