import { __awaiter, __decorate } from "tslib";
import { Auth, Connect, CredentialManager, MessageBuilder, Session, } from '@genesislcap/foundation-comms';
import { sync } from '@genesislcap/foundation-utils';
import { SecondaryRapidColorHEX } from '@genesislcap/foundation-zero';
import { attr, css, customElement, FASTElement, html, observable, when, } from '@microsoft/fast-element';
import { Container } from '@microsoft/fast-foundation';
import { Route, Router } from '@microsoft/fast-router';
import { classNames } from '@microsoft/fast-web-utilities';
import { defaultLoginConfig, hostUrl, LoginConfig } from '../main/config';
import { logger } from '../utils';
/**
 * @internal
 */
export const defaultOnBackMS = 3000;
/**
 * @internal
 * @privateRemarks
 * For services / constructor injection, using @optional(LoginConfig) config = defaultLoginConfig; works as expected.
 * However, for elements, the @optional decorator isn't viable. This base element provides an alternative approach.
 */
export class ConfigHostElement extends FASTElement {
    connectedCallback() {
        this.config = this.container.has(LoginConfig, true)
            ? this.container.get(LoginConfig)
            : defaultLoginConfig;
        super.connectedCallback();
    }
}
__decorate([
    Container
], ConfigHostElement.prototype, "container", void 0);
__decorate([
    observable
], ConfigHostElement.prototype, "config", void 0);
/**
 * @internal
 */
export class BaseRoute extends ConfigHostElement {
    constructor() {
        super(...arguments);
        this.host = '';
        this.organisation = '';
        this.username = '';
        this.email = '';
        this.password = '';
        this.password2 = ''; // < New, confirm etc.
        this.showPasswords = false;
        this.message = '';
        this.hasErrors = false;
        this.isSubmitting = false;
        this.onBack = () => this.onNavigate('login');
        this.onNavigate = (routeName, params) => Route.name.push(Router.find(this), routeName, params);
    }
    connectedCallback() {
        var _a;
        super.connectedCallback();
        this.host = (_a = this.session.getSessionStorageItem('hostUrl')) !== null && _a !== void 0 ? _a : hostUrl;
        /**
         * Fill with default values from config / env vars if needed.
         * For now using CredentialManager based auto-filling.
         * TODO: Review use cases.
         */
        // this.organisation = this.config.fields.organisation?.value;
        // this.username = this.config.fields.username?.value;
        // this.email = this.config.fields.email?.value;
        // this.password = this.config.fields.password?.value;
    }
    onSubmit() {
        return __awaiter(this, void 0, void 0, function* () {
            this.isSubmitting = true;
            this.clearError();
            yield this.ensureConnection();
        });
    }
    ensureConnection() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.connect.isConnected) {
                return true;
            }
            return this.connect.connect(this.host);
        });
    }
    setError(message) {
        this.message = message;
        this.hasErrors = true;
    }
    clearError() {
        this.message = '';
        this.hasErrors = false;
    }
    get orgUsername() {
        return this.config.fields.organisation
            ? `${this.username.trim()}:${this.organisation.trim()}`
            : this.username.trim();
    }
    queueOnBack(delay = defaultOnBackMS) {
        setTimeout(() => {
            this.onBack();
        }, delay);
    }
    autofill() {
        var _a, _b, _c;
        return __awaiter(this, void 0, void 0, function* () {
            let credentialData;
            try {
                credentialData = yield this.credentialManager.getCredentials();
                if (credentialData) {
                    this.username = (_a = credentialData.name) !== null && _a !== void 0 ? _a : '';
                    this.password = (_b = credentialData.password) !== null && _b !== void 0 ? _b : '';
                    if (this.config.fields.organisation) {
                        const combinedCompIdUsername = (_c = credentialData.id) !== null && _c !== void 0 ? _c : '';
                        const valueParts = combinedCompIdUsername.split(':');
                        const orgIdx = valueParts.length === 2 ? 1 : 0;
                        const proposedOrganisation = valueParts[orgIdx];
                        if (proposedOrganisation !== this.username) {
                            this.organisation = proposedOrganisation;
                        }
                    }
                }
            }
            catch (err) {
                logger.error('An error occurred attempting to get and autofill from stored credentials.', err);
            }
            return Promise.resolve(!!credentialData);
        });
    }
    storeCredentials(password = this.password) {
        this.credentialManager.storeCredentials({
            id: this.orgUsername,
            name: this.username,
            password,
        });
    }
    sendMessage(message, successFeedback = '', successCallback) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.ensureConnection();
            this.clearError();
            const result = yield this.connect.send(message);
            if (result.ERROR) {
                this.setError(result.ERROR.map((error) => error.TEXT).toString());
                return;
            }
            this.message = successFeedback;
            successCallback && successCallback(result);
            this.queueOnBack();
        });
    }
}
__decorate([
    Auth
], BaseRoute.prototype, "auth", void 0);
__decorate([
    Connect
], BaseRoute.prototype, "connect", void 0);
__decorate([
    Session
], BaseRoute.prototype, "session", void 0);
__decorate([
    MessageBuilder
], BaseRoute.prototype, "messageBuilder", void 0);
__decorate([
    CredentialManager
], BaseRoute.prototype, "credentialManager", void 0);
__decorate([
    observable
], BaseRoute.prototype, "host", void 0);
__decorate([
    observable
], BaseRoute.prototype, "organisation", void 0);
__decorate([
    observable
], BaseRoute.prototype, "username", void 0);
__decorate([
    observable
], BaseRoute.prototype, "email", void 0);
__decorate([
    observable
], BaseRoute.prototype, "password", void 0);
__decorate([
    observable
], BaseRoute.prototype, "password2", void 0);
__decorate([
    observable
], BaseRoute.prototype, "showPasswords", void 0);
__decorate([
    observable
], BaseRoute.prototype, "message", void 0);
__decorate([
    observable
], BaseRoute.prototype, "hasErrors", void 0);
__decorate([
    observable
], BaseRoute.prototype, "isSubmitting", void 0);
/**
 * @internal
 * @privateRemarks
 * Potential optimisation ahead of workflow / statemachine based smart forms integration, but needs tagFor.
 * Feels a bit at odds with the declarative nature we're aiming for, but will await review.
 */
export const configurableTextField = (name, type, autofocus = false, autocomplete = '', className = '', labelOverride = '', testIdOverride = '', required = true) => {
    // Just pass the FieldConfig instead of name?
    const [fieldName, valueKey] = Array.isArray(name) ? name : [name, name];
    return html `
    ${when((x) => x.config.fields[fieldName], html `
        <zero-text-field
          type="${type}"
          name="${fieldName}"
          id="${fieldName}"
          :value="${sync((x) => x[valueKey])}"
          ?autofocus="${() => autofocus}"
          ?required="${() => required}"
          ${autocomplete ? `autocomplete="${autocomplete}"` : ''}
          data-test-id="${testIdOverride !== null && testIdOverride !== void 0 ? testIdOverride : fieldName}"
          class="${className}"
        >
          <span class="form-title">
            ${(x) => labelOverride !== null && labelOverride !== void 0 ? labelOverride : x.config.fields[fieldName].label}
          </span>
        </zero-text-field>
      `)}
  `;
};
/**
 * @internal
 */
export const organisationField = () => html `
  ${when((x) => x.config.fields.organisation, html `
      <zero-text-field
        type="text"
        name="organisation"
        id="organisation"
        autocomplete="organisation"
        data-test-id="organisation"
        :value="${sync((x) => x.organisation)}"
        autofocus
        class="${(x) => (!x.config.logo ? 'no-top-margin' : '')}"
        required
      >
        <span class="form-title">
          ${(x) => { var _a; return (_a = x.config.fields.organisation.label) !== null && _a !== void 0 ? _a : 'Organisation'; }}
        </span>
      </zero-text-field>
    `)}
`;
/**
 * @internal
 */
export const usernameField = () => html `
  <zero-text-field
    type="text"
    name="username"
    id="username"
    autocomplete="username email"
    data-test-id="username"
    :value="${sync((x) => x.username)}"
    ?autofocus="${(x) => !x.config.fields.organisation}"
    class="${(x) => (!x.config.logo && !x.config.fields.organisation ? 'no-top-margin' : '')}"
    required
  >
    <span class="form-title">${(x) => { var _a; return (_a = x.config.fields.username.label) !== null && _a !== void 0 ? _a : 'Username'; }}</span>
  </zero-text-field>
`;
/**
 * @internal
 */
export const emailField = () => html `
  ${when((x) => x.config.fields.email, html `
      <zero-text-field
        type="email"
        name="email"
        id="email"
        autocomplete="email"
        data-test-id="email"
        :value="${sync((x) => x.email)}"
        required
      >
        <span class="form-title">${(x) => { var _a; return (_a = x.config.fields.email.label) !== null && _a !== void 0 ? _a : 'Email'; }}</span>
      </zero-text-field>
    `)}
`;
/**
 * @internal
 */
export const passwordField = (label) => html `
  <zero-text-field
    type="${(x) => (x.showPasswords ? 'text' : 'password')}"
    name="password"
    id="password"
    autocomplete="current-password"
    data-test-id="password"
    :value="${sync((x) => x.password)}"
    required
  >
    <span class="form-title">${(x) => { var _a; return (_a = label !== null && label !== void 0 ? label : x.config.fields.password.label) !== null && _a !== void 0 ? _a : 'Password'; }}</span>
  </zero-text-field>
`;
const password2FieldMeta = {
    new: ['new-password', 'New Password'],
    confirmation: ['password-confirmation', 'Confirm Password'],
};
/**
 * @internal
 */
export const password2Field = (type) => {
    const [name, label] = password2FieldMeta[type];
    return html `
    <zero-text-field
      type="${(x) => (x.showPasswords ? 'text' : 'password')}"
      name="${name}"
      id="${name}"
      autocomplete="${name}"
      data-test-id="${name}"
      :value="${sync((x) => x.password2)}"
      required
    >
      <span class="form-title">${label}</span>
    </zero-text-field>
  `;
};
/**
 * @internal
 * @privateRemarks
 * Temp optimisation. How we handle images like this needs a broader review.
 */
let BackgroundElement = class BackgroundElement extends ConfigHostElement {
    connectedCallback() {
        super.connectedCallback();
        this.$fastController.addStyles(this.config.background);
    }
};
BackgroundElement = __decorate([
    customElement({
        name: 'login-background',
        template: html `
    <template class="background" part="background" data-test-id="background">
      <slot></slot>
    </template>
  `,
        styles: css `
    :host {
      background-color: var(--foundation-login-background-color, transparent);
      background-position: left top;
      background-repeat: no-repeat;
      background-attachment: fixed;
      background-size: cover;
      background-origin: border-box;
      display: flex;
      flex-direction: column;
      align-items: center;
      width: 100%;
      height: 100%;
      overflow: auto;
    }
    ::slotted(div.container) {
      margin: auto;
    }
  `,
    })
], BackgroundElement);
export { BackgroundElement };
/**
 * @internal
 */
let LogoElement = class LogoElement extends ConfigHostElement {
};
LogoElement = __decorate([
    customElement({
        name: 'login-logo',
        template: html `
    ${when((x) => x.config.logo, html `
        <img class="logo" style="${(x) => x.config.logo.styles}" part="logo" data-test-id="logo" />
      `)}
  `,
        styles: css `
    .logo {
      display: initial;
      margin-bottom: calc(var(--design-unit) * 5px);
      transition: opacity 5s ease-in-out;
      width: auto;
      height: auto;
    }
  `,
    })
], LogoElement);
export { LogoElement };
/**
 * @internal
 */
export const backButton = (binding) => html `
  <zero-button
    appearance="neutral-grey"
    type="button"
    class="action-btn"
    @click=${binding}
    data-test-id="back"
  >
    Back
  </zero-button>
`;
/**
 * @internal
 */
export const submitButton = (label, dataTestId = 'submit', disabled = false) => html `
  <zero-button
    appearance="primary-gradient"
    type="submit"
    class="action-btn"
    data-test-id="${dataTestId}"
    ?disabled="${() => disabled}"
  >
    ${label}
  </zero-button>
`;
/**
 * @internal
 */
export const showPasswordsCheckbox = () => html `
  <zero-checkbox
    ?checked="${sync((x) => x.showPasswords, 'boolean')}"
    name="show-passwords"
    data-test-id="show-passwords"
  >
    Show Passwords
  </zero-checkbox>
`;
/**
 * @internal
 * @privateRemarks
 * Temp optimisation. How we handle images like this needs a broader review.
 */
let MessageElement = class MessageElement extends FASTElement {
    constructor() {
        super(...arguments);
        this.classNames = '';
        this.message = '';
        this.nonError = false;
    }
    nonErrorChanged() {
        this.classNames = classNames('message', ['non-error', this.nonError]);
    }
};
__decorate([
    observable
], MessageElement.prototype, "classNames", void 0);
__decorate([
    attr
], MessageElement.prototype, "message", void 0);
__decorate([
    attr({ mode: 'boolean', attribute: 'non-error' })
], MessageElement.prototype, "nonError", void 0);
MessageElement = __decorate([
    customElement({
        name: 'login-message',
        template: html `
    ${when((x) => x.message, html `
        <span class="${(x) => x.classNames}" part="message" data-test-id="message">
          ${(x) => x.message}
        </span>
      `)}
  `,
        styles: css `
    :host {
      text-align: center;
    }
    .message {
      color: ${SecondaryRapidColorHEX.orange};
      margin-top: calc(var(--design-unit) * 3px);
      white-space: pre-line;
    }
    .message.non-error {
      color: ${SecondaryRapidColorHEX.teal};
    }
  `,
    })
], MessageElement);
export { MessageElement };
/**
 * @internal
 */
let SubmittingIndicator = class SubmittingIndicator extends FASTElement {
    constructor() {
        super(...arguments);
        this.isSubmitting = false;
    }
};
__decorate([
    attr({ mode: 'boolean', attribute: 'is-submitting' })
], SubmittingIndicator.prototype, "isSubmitting", void 0);
SubmittingIndicator = __decorate([
    customElement({
        name: 'submitting-indicator',
        template: html `
    ${when((x) => x.isSubmitting, html `
        <zero-progress-ring
          part="submitting-indicator"
          data-test-id="submitting-indicator"
        ></zero-progress-ring>
      `)}
  `,
        styles: css `
    :host {
      display: flex;
      justify-content: center;
    }
    zero-progress-ring {
      align-self: center;
    }
  `,
    })
], SubmittingIndicator);
export { SubmittingIndicator };
//# sourceMappingURL=base.js.map