import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";
import { when } from "lit/directives/when.js";
import { booleanConverter } from "../../../converters/boolean-converter.js";
import { bindings } from "../../../decorators/bindings.js";
import { errorGuard } from "../../../decorators/error-guard.js";
import { watch } from "../../../decorators/watch.js";
import { withTailwindStyles } from "../../../decorators/with-tailwind-styles.js";
import { multiClassMap, tw } from "../../../directives/multi-class-map.js";
import { InitializeBindingsMixin } from "../../../mixins/bindings-mixin.js";
import { buildCustomEvent, listenOnce } from "../../../utils/event-utils.js";
import { updateBreakpoints } from "../../../utils/replace-breakpoint-utils.js";
import { once, randomID } from "../../../utils/utils.js";
function _ts_decorate(decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : null === desc ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) r = Reflect.decorate(decorators, target, key, desc);
    else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function _ts_metadata(k, v) {
    if ("object" == typeof Reflect && "function" == typeof Reflect.metadata) return Reflect.metadata(k, v);
}
class AtomicModal extends InitializeBindingsMixin(LitElement) {
    connectedCallback() {
        super.connectedCallback();
        document.body.addEventListener('keyup', this.handleCloseOnEscape);
        document.body.addEventListener('touchmove', this.onWindowTouchMove, {
            passive: false
        });
    }
    render() {
        this.updateBreakpoints();
        return html`
      ${when(this.shouldRender, ()=>html`
          <div
            part="backdrop"
            class=${multiClassMap(tw({
                'top-0 right-0 bottom-0 left-0 z-9999': true,
                fixed: 'page' === this.boundary,
                absolute: 'page' !== this.boundary,
                'grid p-6 transition-colors duration-500 ease-in-out grid-template-modal': !this.fullscreen,
                'bg-[rgba(40,40,40,0.8)]': this.isOpen && !this.fullscreen,
                'pointer-events-auto': this.isOpen,
                'pointer-events-none': !this.isOpen
            }))}
            @click="${(e)=>e.target === e.currentTarget && this.close()}"
            data-nosnippet
          >
            <atomic-focus-trap
              class="contents"
              role="dialog"
              aria-modal=${this.isOpen ? 'true' : 'false'}
              aria-labelledby=${this.headerId}
              .source=${this.source}
              .container=${this.container ?? this}
              ${ref(this.focusTrap)}
              .scope=${this.scope}
            >
              ${this.renderContent()}
            </atomic-focus-trap>
          </div>
        `)}
    `;
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        document.body.removeEventListener('keyup', this.handleCloseOnEscape);
        document.body.removeEventListener('touchmove', this.onWindowTouchMove);
    }
    async initialize() {
        if (this.isOpen) {
            this.shouldRender = true;
            await this.handleToggleOpen(this.isOpen);
        }
    }
    async watchToggleOpen(_prev, next) {
        await this.handleToggleOpen(next ?? this.isOpen);
    }
    addModalOpenedClasses() {
        const modalOpenedClass = 'atomic-modal-opened';
        document.body.classList.add(modalOpenedClass);
        this.bindings?.interfaceElement?.classList?.add(modalOpenedClass);
    }
    createExecutionTracker() {
        return ++this.currentWatchToggleOpenExecution;
    }
    async handleModalClose() {
        const executionId = this.createExecutionTracker();
        this.removeModalOpenedClasses();
        this.setFocusTrapActive(false);
        await this.waitForAnimationEnded();
        this.onAnimationEnded();
        if (!this.isExecutionValid(executionId)) return;
        this.shouldRender = false;
    }
    async handleModalOpen() {
        const executionId = this.createExecutionTracker();
        if (!this.shouldRender) {
            this.shouldRender = true;
            await this.updateComplete;
        }
        this.addModalOpenedClasses();
        await this.waitForAnimationEnded();
        if (!this.isExecutionValid(executionId)) return;
        this.setFocusTrapActive(true);
    }
    async handleToggleOpen(isOpen) {
        if (isOpen) await this.handleModalOpen();
        else await this.handleModalClose();
    }
    isExecutionValid(executionId) {
        return executionId === this.currentWatchToggleOpenExecution;
    }
    removeModalOpenedClasses() {
        const modalOpenedClass = 'atomic-modal-opened';
        document.body.classList.remove(modalOpenedClass);
        this.bindings?.interfaceElement?.classList?.remove(modalOpenedClass);
    }
    renderContent() {
        return html`
      <article
        part="container"
        class=${multiClassMap(tw({
            '[grid-area:modal] bg-background text-on-background flex flex-col justify-between overflow-hidden': true,
            'animate-open': this.isOpen,
            'animate-close': !this.isOpen,
            'absolute inset-0': this.fullscreen,
            'rounded shadow': !this.fullscreen
        }))}
        ${ref(this.animatableContainer)}
      >
        <header
          part="header-wrapper"
          class=${multiClassMap(tw({
            'flex flex-col items-center px-6 py-6': true,
            'py-4': !this.fullscreen
        }))}
        >
          <div
            part="header"
            class=${multiClassMap(tw({
            'flex justify-between w-full max-w-lg text-xl': true,
            'font-bold': this.fullscreen
        }))}
            id=${this.headerId}
          >
            <slot name="header"></slot>
          </div>
        </header>
        <hr part="header-ruler" class="border-t border-neutral" />
        <div
          part="body-wrapper"
          class=${multiClassMap(tw({
            'flex w-full grow flex-col items-center overflow-auto [scrollbar-gutter:stable_both-edges]': true,
            'px-6 pt-8 pb-5': this.fullscreen,
            'p-6': !this.fullscreen
        }))}
        >
          <div
            part="body"
            class="w-full max-w-lg"
            @touchmove=${(e)=>this.isOpen && e.stopPropagation()}
          >
            <slot name="body"></slot>
          </div>
        </div>
        <footer
          part="footer-wrapper"
          class=${multiClassMap(tw({
            'z-10 flex flex-col items-center w-full border-t border-neutral bg-background': true,
            'px-6 py-4 shadow-t-lg': this.fullscreen,
            'bg-neutral-light px-4 py-4.5': !this.fullscreen
        }))}
        >
          <div part="footer" class="w-full max-w-lg">
            <slot name="footer"></slot>
          </div>
        </footer>
      </article>
    `;
    }
    setFocusTrapActive(active) {
        if (this.focusTrap.value) this.focusTrap.value.active = active;
    }
    waitForAnimationEnded() {
        return new Promise((resolve)=>{
            if (!this.animatableContainer.value) return void resolve();
            listenOnce(this.animatableContainer.value, 'animationend', ()=>resolve());
        });
    }
    constructor(...args){
        super(...args), this.boundary = 'page', this.close = ()=>{
            this.isOpen = false;
            return false;
        }, this.fullscreen = false, this.isOpen = false, this.onAnimationEnded = ()=>{}, this.animatableContainer = createRef(), this.currentWatchToggleOpenExecution = 0, this.focusTrap = createRef(), this.headerId = randomID('atomic-modal-header-'), this.shouldRender = false, this.updateBreakpoints = once(()=>updateBreakpoints(this)), this.handleCloseOnEscape = (e)=>{
            if (e.key?.toLowerCase() === 'escape') {
                if (this.isOpen) this.dispatchEvent(buildCustomEvent('close'));
                if (this.close) this.close();
                this.isOpen = false;
            }
        }, this.onWindowTouchMove = (e)=>{
            this.isOpen && e.preventDefault();
        };
    }
}
AtomicModal.styles = css`/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */@keyframes scaleUp{0%{opacity:0;transform:scale(.7)translateY(150vh)}to{opacity:1;transform:scale(1)translateY(0)}}@keyframes slideDown{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(150vh)}}article.animate-open{animation:var(--animate-scale-up-modal,scaleUp .5s cubic-bezier(.165,.84,.44,1) forwards)}article.animate-close{animation:var(--animate-slide-down-modal,slideDown .5s linear forwards)}.grid-template-modal{grid-template:". . ."1fr".modal."". . ."3fr/1fr min(30rem,100%) 1fr}`;
_ts_decorate([
    state(),
    _ts_metadata("design:type", "u" < typeof AnyBindings ? Object : AnyBindings)
], AtomicModal.prototype, "bindings", void 0);
_ts_decorate([
    state(),
    _ts_metadata("design:type", "u" < typeof Error ? Object : Error)
], AtomicModal.prototype, "error", void 0);
_ts_decorate([
    property({
        type: String,
        reflect: true
    }),
    _ts_metadata("design:type", String)
], AtomicModal.prototype, "boundary", void 0);
_ts_decorate([
    property({
        type: Object,
        attribute: false
    }),
    _ts_metadata("design:type", Function)
], AtomicModal.prototype, "close", void 0);
_ts_decorate([
    property({
        type: Object,
        attribute: false
    }),
    _ts_metadata("design:type", "u" < typeof HTMLElement ? Object : HTMLElement)
], AtomicModal.prototype, "container", void 0);
_ts_decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter
    })
], AtomicModal.prototype, "fullscreen", void 0);
_ts_decorate([
    property({
        type: Boolean,
        reflect: true,
        attribute: 'is-open',
        converter: booleanConverter
    })
], AtomicModal.prototype, "isOpen", void 0);
_ts_decorate([
    property({
        type: Object,
        attribute: false
    }),
    _ts_metadata("design:type", Function)
], AtomicModal.prototype, "onAnimationEnded", void 0);
_ts_decorate([
    property({
        type: Object,
        attribute: false
    }),
    _ts_metadata("design:type", "u" < typeof HTMLElement ? Object : HTMLElement)
], AtomicModal.prototype, "scope", void 0);
_ts_decorate([
    property({
        type: Object,
        attribute: false
    }),
    _ts_metadata("design:type", "u" < typeof HTMLElement ? Object : HTMLElement)
], AtomicModal.prototype, "source", void 0);
_ts_decorate([
    errorGuard(),
    _ts_metadata("design:type", Function),
    _ts_metadata("design:paramtypes", []),
    _ts_metadata("design:returntype", void 0)
], AtomicModal.prototype, "render", null);
_ts_decorate([
    watch('isOpen', {
        waitUntilFirstUpdate: false
    }),
    _ts_metadata("design:type", Function),
    _ts_metadata("design:paramtypes", [
        Boolean,
        Boolean
    ]),
    _ts_metadata("design:returntype", Promise)
], AtomicModal.prototype, "watchToggleOpen", null);
AtomicModal = _ts_decorate([
    customElement('atomic-modal'),
    bindings(),
    withTailwindStyles
], AtomicModal);
export { AtomicModal };
