import { AESCipherSIVNonce96 } from "../../base/cipher/aes";
import { AbstractCryptoConfig } from "../base";
import { NanoOwnConfigDumpArgs, NanoOwnConfigLoadArgs } from "./args";
import { AccountMasterDeriveKeyV1, TS_WINDOW_V1 } from "../common"
import { AbstractSelfAccountKeyring } from "../../keyring/account_base";
import { makeInfoByteArray, ValueError } from "../../utility"

/**
 * Nano message (request and initial response) encryption: AES-SIV
        KEY: 32B master secret from self kr -> 64B HKDF by context + current time-slice-window
        NONCE: 12B random
 */
export class NanoOwnConfigV1 extends AbstractCryptoConfig<NanoOwnConfigLoadArgs, NanoOwnConfigDumpArgs>{

    public static TS_WINDOW = TS_WINDOW_V1;

    constructor(never_dump: boolean = false){
        super(1, never_dump);
    }

    public load(args: NanoOwnConfigLoadArgs): Promise<Uint8Array>{
        return new Promise((resolve, reject)=>{
            
            let error;
            
            let cipher = args.cipher.slice(this.version.byteLength);
            let timestampArr = NanoOwnConfigV1.TS_WINDOW.windows(args.timestamp);

            let _load = (timestamp_index)=>{
                
                if (timestamp_index >= timestampArr.length){
                    reject(error)
                    return;
                }
                
                let timestamp_window = timestampArr[timestamp_index];
                
                this.derive_key(args.self_kr, timestamp_window).then((derive_key)=>{
                    
                    AESCipherSIVNonce96.decrypt(
                        cipher, 
                        derive_key
                    ).then((res)=>{
                        resolve(res);
                    }).catch((err)=>{
                        if (err instanceof ValueError){
                            error = err;
                            _load(timestamp_index+1);
                        }
                        else{
                            reject(err);
                        }
                    })
                }).catch(reject)

            }

            _load(0);

        });
        
    }

    public dump(args: NanoOwnConfigDumpArgs): Promise<Uint8Array>{
        return this.derive_key(args.self_kr, NanoOwnConfigV1.TS_WINDOW.window(args.timestamp)).then((derive_key)=>{
            return AESCipherSIVNonce96.encrypt(
                args.plain,
                derive_key,
                undefined,
                undefined,
                this.version
            )
        })        
    }

    public derive_key(self_kr: AbstractSelfAccountKeyring, ts_window: Uint8Array){
        return AccountMasterDeriveKeyV1.derive_key(self_kr, makeInfoByteArray(["nano-own-", this.version, "-", ts_window]))
    }
}