import { Builder } from 'strophe.js';
import { Collection, Model } from '@converse/skeletor';
import { getOpenPromise } from '@converse/openpromise';
import BaseMessage from './message.js';

export type MessageAndStanza = {
    message: BaseMessage; // The message object from which the stanza is created and which gets persisted to storage.
    stanza: Builder; // The stanza that will be sent out, as a Strophe.Builder object.
};

export type ReplaceableOpenPromise = ReturnType<typeof getOpenPromise> & {
    replace?: boolean;
};

export type ModelAttributes = Record<string, any>;

export interface ModelOptions {
    collection?: Collection;
    parse?: boolean;
    unset?: boolean;
    silent?: boolean;
}

export type RSMResult = {
    count?: string;
    first?: string;
    last?: string;
};

// Types for mixins.
// -----------------

// Represents the class that will be extended via a mixin.
type Constructor<T = {}> = new (...args: any[]) => T;

export type ModelExtender = Constructor<Model>;

type EncryptionPayloadAttrs = {
    key?: string;
    prekey?: boolean;
    device_id: string;
    payload?: string;
};

export type RetractionAttrs = {
    editable: boolean;
    is_tombstone?: boolean;
    retracted: string;
    retracted_id?: string; // ID of the message being retracted
    retraction_id?: string; // ID of the retraction message
};

export type EncryptionAttrs = {
    encrypted?: EncryptionPayloadAttrs; //  XEP-0384 encryption payload attributes
    is_encrypted: boolean;
    encryption_namespace: string;
};

export type XFormReportedField = {
    var: string;
    label: string;
};

export type XFormResultItemField = {
    var: string;
    value: string;
};

export type XFormOption = {
    value: string;
    label: string;
    selected: boolean;
    required: boolean;
};

export type XFormCaptchaURI = {
    type: string;
    data: string;
};

type XFormListTypes = 'list-single' | 'list-multi';
type XFormJIDTypes = 'jid-single' | 'jid-multi';
type XFormTextTypes = 'text-multi' | 'text-private' | 'text-single';
type XFormDateTypes = 'date' | 'datetime';
type XFormFieldTypes =
    | XFormListTypes
    | XFormJIDTypes
    | XFormTextTypes
    | XFormDateTypes
    | 'fixed'
    | 'boolean'
    | 'url'
    | 'hidden';

export type XFormField = {
    var: string;
    label: string;
    type?: XFormFieldTypes;
    text?: string;
    value?: string;
    required?: boolean;
    checked?: boolean;
    options?: XFormOption[];
    uri?: XFormCaptchaURI;
    readonly: boolean;
};

export type XFormResponseType = 'result' | 'form';

export type XForm = {
    type: XFormResponseType;
    title?: string;
    instructions?: string;
    reported?: XFormReportedField[];
    items?: XFormResultItemField[][];
    fields?: XFormField[];
};

// An object representing XEP-0372 reference data
export type XEP372Reference = {
    begin: number;
    end: number;
    type: string;
    value: string;
    uri: string;
};

export type ErrorExtra = Record<string, string>;

// https://datatracker.ietf.org/doc/html/rfc6120#section-8.3
export type ErrorName =
    | 'bad-request'
    | 'conflict'
    | 'feature-not-implemented'
    | 'forbidden'
    | 'gone'
    | 'internal-server-error'
    | 'item-not-found'
    | 'jid-malformed'
    | 'not-acceptable'
    | 'not-allowed'
    | 'not-authorized'
    | 'payment-required'
    | 'recipient-unavailable'
    | 'redirect'
    | 'registration-required'
    | 'remote-server-not-found'
    | 'remote-server-timeout'
    | 'resource-constraint'
    | 'service-unavailable'
    | 'subscription-required'
    | 'undefined-condition'
    | 'unexpected-request';

export type ErrorType = 'auth' | 'cancel' | 'continue' | 'modify' | 'wait';

// Represents a XEP-0372 reference
export type Reference = {
    begin: number;
    end: number;
    type: string;
    uri: string;
};

export type MessageErrorAttributes = {
    is_error: boolean; // Whether an error was received for this message
    error: string; // The error name
    errors: { name: string; xmlns: string }[];
    error_condition: string; // The defined error condition
    error_text: string; // The error text received from the server
    error_type: string; // The type of error received from the server
};

export type MessageAttributes = EncryptionAttrs &
    MessageErrorAttributes & {
        body: string; // The contents of the <body> tag of the message stanza
        chat_state: string; // The XEP-0085 chat state notification contained in this message
        contact_jid: string; // The JID of the other person or entity
        editable: boolean; // Is this message editable via XEP-0308?
        edited: string; // An ISO8601 string recording the time that the message was edited per XEP-0308
        from: string; // The sender JID
        message?: string; // Used with info and error messages
        fullname: string; // The full name of the sender
        is_archived: boolean; //  Is this message from a XEP-0313 MAM archive?
        is_carbon: boolean; // Is this message a XEP-0280 Carbon?
        is_delayed: boolean; // Was delivery of this message was delayed as per XEP-0203?
        is_encrypted: boolean; //  Is this message XEP-0384  encrypted?
        is_headline: boolean; // Is this a "headline" message?
        is_markable: boolean; // Can this message be marked with a XEP-0333 chat marker?
        is_marker: boolean; // Is this message a XEP-0333 Chat Marker?
        is_only_emojis: boolean; // Does the message body contain only emojis?
        is_spoiler: boolean; // Is this a XEP-0382 spoiler message?
        is_tombstone: boolean; // Is this a XEP-0424 tombstone?
        is_unstyled: boolean; // Whether XEP-0393 styling hints should be ignored
        is_valid_receipt_request: boolean; // Does this message request a XEP-0184 receipt (and is not from us or a carbon or archived message)
        marker: string; // The XEP-0333 Chat Marker value
        marker_id: string; // The `id` attribute of a XEP-0333 chat marker
        msgid: string; // The root `id` attribute of the stanza
        nick: string; // The roster nickname of the sender
        ogp_for_id?: string; // Used for Open Graph Metadata support for unfurls
        oob_desc: string; // The description of the XEP-0066 out of band data
        oob_url: string; // The URL of the XEP-0066 out of band data
        origin_id: string; // The XEP-0359 Origin ID
        plaintext: string; // The decrypted text of this message, in case it was encrypted.
        receipt_id: string; // The `id` attribute of a XEP-0184 <receipt> element
        received: string; // An ISO8601 string recording the time that the message was received
        references: Array<Reference>; // A list of objects representing XEP-0372 references
        replace_id: string; // The `id` attribute of a XEP-0308 <replace> element
        retracted: string; // An ISO8601 string recording the time that the message was retracted
        retracted_id: string; // The `id` attribute of a XEP-424 <retracted> element
        sender: 'me' | 'them'; // Whether the message was sent by the current user or someone else
        spoiler_hint: string; //  The XEP-0382 spoiler hint
        stanza_id: string; // The XEP-0359 Stanza ID. Note: the key is actualy `stanza_id ${by_jid}` and there can be multiple.
        subject: string; // The <subject> element value
        thread: string; // The <thread> element value
        time: string; // The time (in ISO8601 format), either given by the XEP-0203 <delay> element, or of receipt.
        to: string; // The recipient JID
        type: string; // The type of message
    };

export type FileUploadMessageAttributes = {
    body: string;
    message: string;
    oob_url: string;
    upload: 'success' | 'failure';
};

export type MessageMarkerType = 'displayed' | 'received' | 'acknowledged';
export type ChatStateType = 'active' | 'composing' | 'paused' | 'inactive' | 'gone';
