import { type TemplateResult, noChange } from 'lit';
import { AsyncDirective, directive } from 'lit/async-directive.js';

/**
 * Resolves a promise and sets the value of the directive
 */
class ResolveDirective extends AsyncDirective {
  promise: Promise<unknown> | undefined;
  hasPromiseSettled: boolean = false;

  render(promise: Promise<unknown>) {
    if (this.promise !== promise) {
      this.promise = promise;

      if (this.isConnected) {
        this.handlePromise(promise);
      }
    }

    return noChange;
  }

  handlePromise(promise: Promise<unknown>) {
    this.hasPromiseSettled = false;

    promise.then((value) => {
      if (this.promise === promise && !this.hasPromiseSettled) {
        this.setValue(value);
        this.hasPromiseSettled = true;
      }
    }).catch((error) => {
      if (this.promise === promise && !this.hasPromiseSettled) {
        this.setValue(error);
        this.hasPromiseSettled = true;
      }
    });
  }

  disconnected() {
    this.hasPromiseSettled = true; // prevent setting value if the promise settles after disconnection
  }

  reconnected() {
    if (!this.hasPromiseSettled) {
      this.handlePromise(this.promise!);
    }
  }
}

export const resolve = directive(ResolveDirective);
export const resolveExec = (funcArg: () => Promise<TemplateResult | unknown>) => {
  return resolve(funcArg());
}