import type { StateFileToIniParserMap } from '@app/store/types.js';
import { type IniStringBoolean, type IniStringBooleanOrAuto } from '@app/core/types/ini.js';
import { toNumber } from '@app/core/utils/index.js';
import {
    iniBooleanOrAutoToJsBoolean,
    iniBooleanToJsBoolean,
} from '@app/core/utils/parsers/ini-boolean-parser.js';
import { ArrayState } from '@app/unraid-api/graph/resolvers/array/array.model.js';
import { DiskFsType } from '@app/unraid-api/graph/resolvers/disks/disks.model.js';
import {
    RegistrationState,
    RegistrationType,
} from '@app/unraid-api/graph/resolvers/registration/registration.model.js';
import { ConfigErrorState } from '@app/unraid-api/graph/resolvers/vars/vars.model.js';

/**
 * Unraid registration check
 */
type RegistrationCheck =
    /** Key file is missing. */
    | 'ENOKEYFILE2'
    /** Everything is fine. */
    | '';

export type VarIni = {
    bindMgt: IniStringBooleanOrAuto;
    cacheNumDevices: string;
    cacheSbNumDisks: string;
    comment: string;
    configValid: string;
    configErrorState: string;
    csrfToken: string;
    defaultFormat: string;
    defaultFsType: string;
    deviceCount: string;
    domain: string;
    domainLogin: string;
    domainShort: string;
    flashGuid: string;
    flashProduct: string;
    flashVendor: string;
    fsCopyPrcnt: string;
    fsNumMounted: string;
    fsNumUnmountable: string;
    fsProgress: string;
    fsState: string;
    fsUnmountableMask: string;
    fuseDirectio: string;
    fuseDirectioDefault: string;
    fuseDirectioStatus: string;
    fuseRemember: string;
    fuseRememberDefault: string;
    fuseRememberStatus: string;
    hideDotFiles: string;
    localMaster: string;
    localTld: string;
    luksKeyfile: string;
    maxArraysz: string;
    maxCachesz: string;
    mdColor: string;
    mdNumDisabled: string;
    mdNumDisks: string;
    mdNumErased: string;
    mdNumInvalid: string;
    mdNumMissing: string;
    mdNumNew: string;
    mdNumStripes: string;
    mdNumStripesDefault: string;
    mdNumStripesStatus: string;
    mdResync: string;
    mdResyncAction: string;
    mdResyncCorr: string;
    mdResyncDb: string;
    mdResyncDt: string;
    mdResyncPos: string;
    mdResyncSize: string;
    mdState: string;
    mdSyncThresh: string;
    mdSyncThreshDefault: string;
    mdSyncThreshStatus: string;
    mdSyncWindow: string;
    mdSyncWindowDefault: string;
    mdSyncWindowStatus: string;
    mdVersion: string;
    mdWriteMethod: string;
    mdWriteMethodDefault: string;
    mdWriteMethodStatus: string;
    name: string;
    nrRequests: string;
    nrRequestsDefault: string;
    ntpServer1: string;
    ntpServer2: string;
    ntpServer3: string;
    ntpServer4: string;
    pollAttributes: string;
    pollAttributesDefault: string;
    pollAttributesStatus: string;
    port: string;
    portssh: string;
    portssl: string;
    porttelnet: string;
    queueDepth: string;
    regCheck: RegistrationCheck;
    regFile: string;
    regGen: string;
    regGuid: string;
    regTm: string;
    regTm2: string;
    regExp?: string;
    regTo: string;
    regTy: string;
    regState: string;
    safeMode: string;
    sbClean: string;
    sbEvents: string;
    sbName: string;
    sbNumDisks: string;
    sbState: string;
    sbSynced: string;
    sbSynced2: string;
    sbSyncErrs: string;
    sbSyncExit: string;
    sbUpdated: string;
    sbVersion: string;
    security: string;
    shareAvahiEnabled: string;
    shareAvahiSmbModel: string;
    shareAvahiSmbName: string;
    shareCacheEnabled: string;
    shareCacheFloor: string;
    shareCount: string;
    shareDisk: string;
    shareInitialGroup: string;
    shareInitialOwner: string;
    shareMoverActive: string;
    shareMoverLogging: string;
    shareMoverSchedule: string;
    shareNfsCount: string;
    shareNfsEnabled: string;
    shareSmbCount: string;
    shareSmbEnabled: string;
    shareUser: string;
    shareUserExclude: string;
    shutdownTimeout: string;
    spindownDelay: string;
    spinupGroups: string;
    startArray: string;
    startMode: string;
    startPage: string;
    sysArraySlots: string;
    sysCacheSlots: string;
    sysFlashSlots: string;
    sysModel: string;
    timeZone: string;
    useNtp: IniStringBoolean;
    useSsh: IniStringBoolean;
    useSsl: IniStringBooleanOrAuto;
    useTelnet: string;
    version: string;
    workgroup: string;
    useUpnp: IniStringBoolean;
};

const safeParseMdState = (mdState: string | undefined): ArrayState => {
    if (!mdState || typeof mdState !== 'string') {
        return ArrayState.STOPPED;
    }
    const stateUpper = mdState.toUpperCase();
    const attemptedParse =
        ArrayState[stateUpper.startsWith('ERROR') ? stateUpper.split(':')[1] : stateUpper];

    if (!attemptedParse) {
        return ArrayState.STOPPED;
    }
    return attemptedParse;
};

export const convertconfigErrorStateToEnum = (configErrorState: string): ConfigErrorState => {
    return ConfigErrorState[configErrorState.toUpperCase()];
};

export const parse: StateFileToIniParserMap['var'] = (iniFile) => {
    return {
        ...iniFile,
        defaultFsType: DiskFsType[iniFile.defaultFsType] || DiskFsType.XFS,
        mdState: safeParseMdState(iniFile.mdState),
        bindMgt: iniBooleanOrAutoToJsBoolean(iniFile.bindMgt) ?? null,
        cacheNumDevices: toNumber(iniFile.cacheNumDevices),
        cacheSbNumDisks: toNumber(iniFile.cacheSbNumDisks),
        configValid: iniBooleanToJsBoolean(iniFile.configValid, false),
        configErrorState: iniBooleanToJsBoolean(iniFile.configValid, false)
            ? null
            : convertconfigErrorStateToEnum(iniFile.configValid),
        deviceCount: toNumber(iniFile.deviceCount),
        fsCopyPrcnt: toNumber(iniFile.fsCopyPrcnt),
        fsNumMounted: toNumber(iniFile.fsNumMounted),
        fsNumUnmountable: toNumber(iniFile.fsNumUnmountable),
        hideDotFiles: iniBooleanToJsBoolean(iniFile.hideDotFiles, false),
        localMaster: iniBooleanToJsBoolean(iniFile.localMaster, false),
        maxArraysz: toNumber(iniFile.maxArraysz),
        maxCachesz: toNumber(iniFile.maxCachesz),
        mdNumDisabled: toNumber(iniFile.mdNumDisabled),
        mdNumDisks: toNumber(iniFile.mdNumDisks),
        mdNumErased: toNumber(iniFile.mdNumErased),
        mdNumInvalid: toNumber(iniFile.mdNumInvalid),
        mdNumMissing: toNumber(iniFile.mdNumMissing),
        mdNumNew: toNumber(iniFile.mdNumNew),
        mdNumStripes: toNumber(iniFile.mdNumStripes),
        mdNumStripesDefault: toNumber(iniFile.mdNumStripesDefault),
        mdResync: toNumber(iniFile.mdResync),
        mdResyncPos: toNumber(iniFile.mdResyncPos),
        mdResyncSize: toNumber(iniFile.mdResyncSize),
        mdSyncThresh: toNumber(iniFile.mdSyncThresh),
        mdSyncThreshDefault: toNumber(iniFile.mdSyncThreshDefault),
        mdSyncWindow: toNumber(iniFile.mdSyncWindow),
        mdSyncWindowDefault: toNumber(iniFile.mdSyncWindowDefault),
        mdWriteMethod: toNumber(iniFile.mdWriteMethod),
        nrRequests: toNumber(iniFile.nrRequests),
        nrRequestsDefault: toNumber(iniFile.nrRequestsDefault),
        port: toNumber(iniFile.port),
        portssh: toNumber(iniFile.portssh),
        portssl: toNumber(iniFile.portssl),
        porttelnet: toNumber(iniFile.porttelnet),
        regCheck: iniFile.regCheck === '' ? 'Valid' : 'Error',
        regTy: RegistrationType[iniFile.regTy?.toUpperCase()] ?? RegistrationType.INVALID,
        regExp: iniFile.regExp ?? null,
        // Make sure to use a || not a ?? as regCheck can be an empty string
        regState:
            RegistrationState[(iniFile.regCheck || iniFile.regTy || '').toUpperCase()] ??
            RegistrationState.EGUID,
        safeMode: iniBooleanToJsBoolean(iniFile.safeMode, false),
        sbClean: iniBooleanToJsBoolean(iniFile.sbClean, false),
        sbEvents: toNumber(iniFile.sbEvents),
        sbNumDisks: toNumber(iniFile.sbNumDisks),
        sbSynced: toNumber(iniFile.sbSynced),
        sbSynced2: toNumber(iniFile.sbSynced2),
        sbSyncErrs: toNumber(iniFile.sbSyncErrs),
        shareAvahiEnabled: iniBooleanToJsBoolean(iniFile.shareAvahiEnabled, false),
        shareCacheEnabled: iniBooleanToJsBoolean(iniFile.shareCacheEnabled, false),
        shareCount: toNumber(iniFile.shareCount),
        shareMoverActive: iniBooleanToJsBoolean(iniFile.shareMoverActive, false),
        shareMoverLogging: iniBooleanToJsBoolean(iniFile.shareMoverLogging, false),
        shareNfsCount: toNumber(iniFile.shareNfsCount),
        shareNfsEnabled: iniBooleanToJsBoolean(iniFile.shareNfsEnabled, false),
        shareSmbCount: toNumber(iniFile.shareSmbCount),
        shareSmbEnabled: ['yes', 'ads'].includes(iniFile.shareSmbEnabled),
        shareSmbMode: iniFile.shareSmbEnabled === 'ads' ? 'active-directory' : 'workgroup',
        shutdownTimeout: toNumber(iniFile.shutdownTimeout),
        spindownDelay: toNumber(iniFile.spindownDelay),
        spinupGroups: iniBooleanToJsBoolean(iniFile.spinupGroups, false),
        startArray: iniBooleanToJsBoolean(iniFile.startArray, false),
        sysArraySlots: toNumber(iniFile.sysArraySlots),
        sysCacheSlots: toNumber(iniFile.sysCacheSlots),
        sysFlashSlots: toNumber(iniFile.sysFlashSlots),
        useNtp: iniBooleanToJsBoolean(iniFile.useNtp, false),
        useSsh: iniBooleanToJsBoolean(iniFile.useSsh, false),
        useSsl: iniBooleanOrAutoToJsBoolean(iniFile.useSsl) ?? null,
        useTelnet: iniBooleanToJsBoolean(iniFile.useTelnet, false),
        useUpnp: iniBooleanToJsBoolean(iniFile.useUpnp, false),
    };
};
