import { OutputSuccess } from '../transform/types.js'
import { typeDef, name } from './common.js'
import { Validation } from 'yaschva'

const getTokenDefaultPath = '../getToken'
const containsOptional = (input: Validation) =>
  (Array.isArray(input) && input.some(y => y === '?')) ||
   input === '?'

const allOptional = (input: Validation) =>
  Object.values(input).every(containsOptional)

export const client = (input: OutputSuccess[], getTokenPath?:string): string => {
  const functions = input.map(x => {
    const url =
      x.method === 'GET' ? `\`/api/${x.name}?\${serialize(input || {})}\`` : `"/api/${x.name}"`
    const fn = `
    const valid = validate(contracts.${name(x)}.arguments, input);
      if (valid.result === "fail") throw valid;
    return fetch(${url}, {
      method: "${x.method}",
      headers: await defaultHeader(),
      body: ${x.method === 'GET' ? 'undefined' : 'JSON.stringify(input)'},
      signal
    }).then(x=> x.json())
    .then(x=> {
      if(x.errorType){throw x}
      const r = validate(contracts.${name(x)}.returns, x);
      if(r.result ==="fail") throw r; return x})\n`

    return `export const ${name(x)} =
      async (input: ${name(x)}Argument${allOptional(x.arguments) ? ' = {}' : ''},
        signal: AbortSignal | undefined = undefined):
       Promise<${name(x)}Returns> => {\n${fn}};\n`
  }).join('\n')

  const contractDescription = input.map(x => `${name(x)}: {
    name: "${x.name}",
    authentication: ${JSON.stringify(x.authentication, undefined, 2)},
    type: "${x.method}",
    arguments: ${JSON.stringify(x.arguments)},
    returns: ${JSON.stringify(x.returns)}}`).join(',')

  const fileContent = `/* eslint-disable max-lines */
/**********************************************
  DO NOT EDIT THIS FILE, IT WILL BE OVERRIDDEN
***********************************************/

  import { validate, Validation } from "yaschva";
  import { deepFreeze, serialize } from "microtil";
  import getToken from "${getTokenPath ?? getTokenDefaultPath}"
  const defaultHeader = async () => {
    const token = await getToken();
    const headers =  {
      'Authorization': \`Bearer \${token}\`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }

    if(!token) delete headers.Authorization
    return headers;
  };

    export const contracts: {[key:string]:{
      name:string, type:string, authentication:any,
      arguments: Validation, returns:Validation}} =
        deepFreeze({${contractDescription}});

    ${typeDef(input)}
    ${functions}
    export const functions = {${input.map(x => `${name(x)}: ${name(x)}`)}}
  `
  return fileContent
}

export default client
