import utils from "./utils/ecUtils";
import {
    AllowanceInput,
    AllowanceInvalidInput,
    AllowanceInvalidResp,
    AllowanceQueryInput,
    AllowanceQueryOutput,
    AllowanceResp,
    CheckBarcodeInput,
    CheckBarcodeOutput,
    CheckLoveCodeInput,
    CheckLoveCodeOutput,
    ECPayInstance,
    GetAllowanceInvalidInput,
    GetAllowanceInvalidOutput,
    GetCompanyNameByTaxIDInput,
    GetCompanyNameByTaxIDOutput,
    GetInvalidInput,
    GetInvalidOutput,
    GetIssueListOutput,
    InvoiceInvalidInput,
    InvoiceInvalidResp,
    InvoiceNotifyInput,
    InvoiceNotifyOutput,
    InvoicePrintReq,
    InvoicePrintResp,
    IssueData,
    IssueListQuery,
    IssueResp,
    QueryInvoiceResp,
    QueryWithInvoiceNumberAndDate,
    QueryWithRelateNumber
} from "./types";


const doCommand = async (url: string, input: any, init: any,opt?:{
    plain?: boolean  // 有些Response是沒有加密的  所以要用plain來表示回傳的資料是否需要解密,GetIssueList就是一個例子
}) => {
    const {encode,decode} = utils(init)
    const encryptedData = encode(input)
    const resp = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': "application/json"
        },
        body: JSON.stringify({
            MerchantID: init.MerchantID,
            RqHeader: {
                Timestamp: Date.now()
            },
            Data: encryptedData
        })
    })

    if(opt?.plain){
        const ret = await resp.json()
        return ret
    }
    const json = await resp.json()
    const {Data} = json
    const decryptedData = decode(Data)
    const data = JSON.parse(decryptedData)
    if (data.RtnCode != 1) {
        throw new Error(data.RtnMsg)
    }
    return {
        ...data,
    }
}

export default function ECPay(init: {
    MerchantID: string,
    HashKey: string,
    HashIV: string,
    BaseURL: string, // https://einvoice-stage.ecpay.com.tw
}): ECPayInstance {

    const allowance = async (input: AllowanceInput): Promise<AllowanceResp> => {
        const url = `${init.BaseURL}/B2CInvoice/Allowance`
        const {encode} = utils(init)
        const encryptedData = encode(input)
        const resp = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': "application/json"
            },
            body: JSON.stringify({
                MerchantID: init.MerchantID,
                RqHeader: {
                    Timestamp: Date.now()
                },
                Data: encryptedData
            })
        })
        const json = await resp.json()
        const {Data} = json
        const decryptedData = utils(init).decode(Data)
        const data = JSON.parse(decryptedData)
        if (data.RtnCode != 1) {
            throw new Error(data.RtnMsg)
        }
        return {
            ...json,
            DecryptData: data,
        }
    }


    const issue = async (issueData: IssueData): Promise<IssueResp> => {
        const url = `${init.BaseURL}/B2CInvoice/Issue`
        const {encode} = utils(init)
        const encryptedData = encode(issueData)
        const resp = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': "application/json"
            },
            body: JSON.stringify({
                MerchantID: init.MerchantID,
                RqHeader: {
                    Timestamp: Date.now()
                },
                Data: encryptedData
            })
        })
        const json = await resp.json()
        const {Data} = json
        const decryptedData = utils(init).decode(Data)
        const data = JSON.parse(decryptedData)
        if (data.RtnCode != 1) {
            throw new Error(data.RtnMsg)
        }
        return {
            ...json,
            DecryptedData: data,
        }
    }

    const allowanceInvalid = async (input: AllowanceInvalidInput): Promise<AllowanceInvalidResp> => {
        const url = `${init.BaseURL}/B2CInvoice/AllowanceInvalid`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }

    const invoiceInvalid = async (input: InvoiceInvalidInput): Promise<InvoiceInvalidResp> => {
        const url = `${init.BaseURL}/B2CInvoice/Invalid`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }

    }

    const getIssueList = async (input: IssueListQuery): Promise<GetIssueListOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/GetIssueList`
        const result = await doCommand(url, input, init,{
            plain:true
        })
        return {
            ...result.Data,
        }
    }

    const invoicePrint = async (input: InvoicePrintReq): Promise<InvoicePrintResp> => {
        const url = `${init.BaseURL}/B2CInvoice/InvoicePrint`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }


    }

    const getIssue = async (input: QueryWithRelateNumber | QueryWithInvoiceNumberAndDate): Promise<QueryInvoiceResp> => {
        const url = `${init.BaseURL}/B2CInvoice/GetIssue`
        const {encode} = utils(init)
        const encryptedData = encode(input)
        const resp = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': "application/json"
            },
            body: JSON.stringify({
                MerchantID: init.MerchantID,
                RqHeader: {
                    Timestamp: Date.now()
                },
                Data: encryptedData
            })
        })
        const json = await resp.json()
        const {Data} = json
        const decryptedData = utils(init).decode(Data)
        const data = JSON.parse(decryptedData)
        if (data.RtnCode != 1) {
            throw new Error(data.RtnMsg)
        }
        return {
            ...json,
            DecryptData: data
        }
    }

    const getAllowanceList = async (input: AllowanceQueryInput): Promise<AllowanceQueryOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/GetAllowanceList`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }

    const invoiceNotify = async (input: InvoiceNotifyInput): Promise<InvoiceNotifyOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/InvoiceNotify`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }

    const getAllowanceInvalids  = async (input: GetAllowanceInvalidInput): Promise<GetAllowanceInvalidOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/AllowanceInvalid`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }

    const getInvalids = async (input: GetInvalidInput): Promise<GetInvalidOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/GetInvalid`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }

    }

    const getCompanyNameByTaxID = async (input: GetCompanyNameByTaxIDInput): Promise<GetCompanyNameByTaxIDOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/GetCompanyNameByTaxID`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }

    const checkBarcode = async (input: CheckBarcodeInput): Promise<CheckBarcodeOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/CheckBarcode`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }

    const checkLoveCode = async (input: CheckLoveCodeInput): Promise<CheckLoveCodeOutput> => {
        const url = `${init.BaseURL}/B2CInvoice/CheckLoveCode`
        const result = await doCommand(url, input, init)
        return {
            ...result,
        }
    }


    return {
        // 檢查愛心碼
        CheckLoveCode(input: CheckLoveCodeInput): Promise<CheckLoveCodeOutput> {
            return checkLoveCode(input)
        },
        // 檢查手機條碼
        CheckBarcode(input: CheckBarcodeInput): Promise<CheckBarcodeOutput> {
            return checkBarcode(input)
        },
        // 檢查統一編號
        GetCompanyNameByTaxID(input: GetCompanyNameByTaxIDInput): Promise<GetCompanyNameByTaxIDOutput> {
            return getCompanyNameByTaxID(input)
        },
        // 取得作廢發票
        GetInvalid(input: GetInvalidInput): Promise<GetInvalidOutput> {
            return getInvalids(input)
        },
        // 取得作廢折讓單
        GetAllowanceInvalid(input: GetAllowanceInvalidInput): Promise<GetAllowanceInvalidOutput> {
            return getAllowanceInvalids(input)
        },
        // 發票通知
        InvoiceNotify(input: InvoiceNotifyInput): Promise<InvoiceNotifyOutput> {
            return invoiceNotify(input)
        },
        // 取得所有折讓單
        GetAllowanceList(input: AllowanceQueryInput): Promise<AllowanceQueryOutput> {
            return getAllowanceList(input)
        },
        // 取得單筆折讓單
        Allowance(input: AllowanceInput): Promise<AllowanceResp> {
            return allowance(input)
        },
        // 作廢折讓單
        AllowanceInvalid(input: AllowanceInvalidInput): Promise<AllowanceInvalidResp> {
            return allowanceInvalid(input)
        },
        // 取得發票
        GetIssue(input: QueryWithRelateNumber | QueryWithInvoiceNumberAndDate): Promise<QueryInvoiceResp> {
            return getIssue(input)
        },
        // 查詢多筆發票
        GetIssueList(input: IssueListQuery): Promise<GetIssueListOutput> {
            return getIssueList(input)
        },
        // 作廢發票
        Invalid(input: InvoiceInvalidInput): Promise<InvoiceInvalidResp> {
            return invoiceInvalid(input)
        },
        // 發票列印
        InvoicePrint(input: InvoicePrintReq): Promise<InvoicePrintResp> {
            return invoicePrint(input)
        },
        // 開立發票
        Issue(input: IssueData): Promise<IssueResp> {
            return issue(input)
        }
    }

}