
import { getOrigin, IActionEmailData, isDevelopment } from '@websolutespa/bom-core';
import { IEmailComponent, IEmailLayoutComponent, renderEmail } from '@websolutespa/bom-mixer-emails';
import { LabelService } from '@websolutespa/bom-mixer-hooks';
import { getLayout, getSession } from '@websolutespa/bom-mixer-models/server';
import { remoteApiGet, remoteApiPatch, remoteApiPost, StoreStrategy, storeStrategy } from '@websolutespa/bom-mixer-store';
import { apiHandler } from '@websolutespa/bom-mixer-store/server';
import { NextApiRequest, NextApiResponse } from 'next';
import { IEndUserResponse, IUser, IUserForgot, IUserForgotAction, IUserRegister, IUserRegisterAction } from '../user/user';
import { AuthDecorateCallback, ForgotConfirmProps, RegisterConfirmProps } from './auth';
import { getAuth, getCurrentUser } from './auth.service';

export function authRegisterHandler({
  Component,
  Layout,
  from = 'no-reply@mixer.com',
  bcc = 'lzampetti@gmail.com',
}: {
  Component: IEmailComponent<RegisterConfirmProps>;
  Layout: IEmailLayoutComponent;
  from?: string; // !!! todo override from app settings in send mail
  bcc?: string;
}) {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    post: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const locale = request.query.locale as (string | undefined);
        const market = request.query.market as (string | undefined);
        if (!market || !locale) {
          return response.status(400).json({ message: 'Missing market or locale' });
        }
        const props: IUserRegister = request.body;
        // console.log('POST > api/auth/register', props);
        if (props.checkField !== null) {
          return response.status(401).json({ message: 'Unauthorized' });
        }
        const origin = isDevelopment ? '' : getOrigin(request.headers);
        const layout = await getLayout(market, locale);
        const labelService = new LabelService(layout.labels);
        /**
         * REGISTER
         */
        const subject = labelService.getLabel('email.register.subject');
        const html = await renderEmail<RegisterConfirmProps>({
          Component,
          Layout,
          origin,
          layout,
          props: props as RegisterConfirmProps,
          preview: subject,
          optInUrl: `${origin}/api/auth/register/optin?market=${layout.market}&locale=${layout.locale}&actionSlug={actionSlug}&actionId={actionId}`,
          /*
          optOutUrl: `${origin}/api/auth/register/optout?market=${layout.market}&locale=${layout.locale}&actionSlug={actionSlug}&actionId={actionId}`,
          viewOnWebUrl: `${origin}/api/auth/register?market=${layout.market}&locale=${layout.locale}&actionSlug={actionSlug}&actionId={actionId}`,
          */
        });
        const emailData: IActionEmailData[] = [{
          to: props.email,
          from,
          bcc,
          subject,
          html,
        }];
        if (storeStrategy !== StoreStrategy.Mock) {
          const action: IUserRegisterAction = {
            ...props,
            consentPreferences: Object.entries(props).filter(([k, v]) => ['privacy'].includes(k) && v === true).map(([k, v]) => k),
            emailData,
          };
          // console.log('action', action, process.env.STORE_API_KEY);
          const result = await remoteApiPost(`/register_action?market=${market}&locale=${locale}`, action);
          // console.log(result);
          response.status(200).json(result);
        } else {
          response.status(200).json(html);
        }
      } catch (error) {
        response.status(400).json(error);
      }
    },
    get: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const locale = request.query.locale as (string | undefined);
        const market = request.query.market as (string | undefined);
        let actionSlug = request.query.actionSlug as (string | undefined);
        let actionId = request.query.actionId as (string | undefined);
        if (!market || !locale) {
          return response.status(400).json({ message: 'Missing market or locale' });
        }
        /*
        if (!actionSlug) {
          return response.status(400).json({ message: 'Missing actionSlug' });
        }
        if (!actionId) {
          return response.status(400).json({ message: 'Missing actionId' });
        }
        */
        const origin = isDevelopment ? '' : getOrigin(request.headers);
        const layout = await getLayout(market, locale);
        const labelService = new LabelService(layout.labels);
        /**
         * REGISTER
         */
        const subject = labelService.getLabel('email.register.subject');
        const props: RegisterConfirmProps = actionSlug && actionId ? (
          await remoteApiGet<RegisterConfirmProps>(`/${actionSlug}/${actionId}?market=${market}&locale=${locale}`)
        ) : ({
          firstName: 'John',
          lastName: 'Appleseed',
          email: 'john@appleseed.com',
          password: '123456Aa!',
        } as RegisterConfirmProps);
        actionSlug = actionSlug || '{actionSlug}';
        actionId = actionId || '{actionId}';
        const html = await renderEmail<RegisterConfirmProps>({
          Component,
          Layout,
          origin,
          layout,
          props,
          preview: subject,
          optInUrl: `${origin}/api/auth/register/optin?market=${layout.market}&locale=${layout.locale}&actionSlug=${actionSlug}&actionId=${actionId}`,
          /*
          optOutUrl: `${origin}/api/auth/register/optout?market=${layout.market}&locale=${layout.locale}&actionSlug=${actionSlug}&actionId=${actionId}`,
          viewOnWebUrl: `${origin}/api/auth/register?market=${layout.market}&locale=${layout.locale}&actionSlug=${actionSlug}&actionId=${actionId}`,
          */
        });
        response.status(200).send(html);
      } catch (error) {
        response.status(400).json(error);
      }
    },
  });
}

export function authRegisterOptinHandler() {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    get: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const locale = request.query.locale as (string | undefined);
        const market = request.query.market as (string | undefined);
        const actionSlug = request.query.actionSlug as (string | undefined);
        const actionId = request.query.actionId as (string | undefined);
        if (!market || !locale) {
          return response.status(400).json({ message: 'Missing market or locale' });
        }
        if (!actionSlug) {
          return response.status(400).json({ message: 'Missing actionSlug' });
        }
        if (!actionId) {
          return response.status(400).json({ message: 'Missing actionId' });
        }
        const origin = isDevelopment ? '' : getOrigin(request.headers);
        const layout = await getLayout(market, locale);
        const optin = await remoteApiGet<RegisterConfirmProps>(`/actions/optin/${actionId}/${actionSlug}?market=${market}&locale=${locale}`);
        const redirectUrl = `${layout.topLevelHrefs.register}?optin=true`;
        response.redirect(redirectUrl);
      } catch (error) {
        response.status(400).json(error);
      }
    },
  });
}

export function authLoginHandler(
  callback?: AuthDecorateCallback
) {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    post: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const body = request.body;
        let user: IUser | undefined = undefined;
        let token: string | undefined = undefined;
        let exp: number | undefined = undefined;
        if (storeStrategy !== StoreStrategy.Mock) {
          const result = await remoteApiPost<IEndUserResponse>('/end_users/login', {
            email: body.email,
            password: body.password,
          });
          // console.log('loginPost.result', result);
          if (result.user && result.token) {
            /*
            user = Object.fromEntries(
              Object.entries(result.user)
                .filter(kv => ['id', 'email', 'firstName', 'lastName'].includes(kv[0]))
            ) as IUser;
            */
            user = result.user;
            token = result.token;
            exp = result.exp;
          }
        } else {
          if (body.email === 'john@appleseed.com' && body.password === '123456Aa!') {
            user = {
              id: 1,
              firstName: 'John',
              lastName: 'Appleseed',
              email: body.email,
              roles: ['guest', 'user'],
              consentPreferences: [],
            };
            token = 'token';
            exp = Math.floor(new Date().getTime() / 1000) + (7 * 24 * 60 * 60);
          }
        }
        if (typeof callback === 'function') {
          user = await callback(user, request, response);
        }
        const session = await getSession(request, response);
        session.token = token;
        session.exp = exp;
        await session.save();
        if (user) {
          // console.log('api/auth/login', 'success', user);
          response.status(200).json(user);
        } else {
          // console.log('api/auth/login', 'unauthorized');
          response.status(401).json(null);
        }
      } catch (error: any) {
        console.log('api/auth/login', 'error', error.status, error.statusText);
        response.status(401).json(null);
      }
    },
  });
}

export function authLogoutHandler() {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    get: async (request: NextApiRequest, response: NextApiResponse) => {
      const session = await getSession(request, response);
      if (storeStrategy !== StoreStrategy.Mock && session.token) {
        try {
          const result = await remoteApiPost('/end_users/logout', {}, {
            headers: {
              Authorization: `JWT ${session.token}`,
            },
          });
          console.log('logoutGet', result);
        } catch (error: any) {
          console.log('logoutGet.error', error.status, error.statusText);
        }
      }
      session.token = undefined;
      session.exp = undefined;
      await session.save();
      session.destroy();
      response.status(200).json(null);
    },
  });
}

export function authMeHandler(
  callback?: AuthDecorateCallback
) {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    get: async (request: NextApiRequest, response: NextApiResponse) => {
      const user = await getCurrentUser(request, response, callback);
      response.status(200).json(user || null);
    },
  });
}

export function authPasswordHandler() {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    post: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const body = request.body;
        let user: IUser | undefined = undefined;
        let token: string | undefined = undefined;
        let exp: number | undefined = undefined;
        const auth = await getAuth(request, response);
        if (auth.token) {
          if (storeStrategy !== StoreStrategy.Mock) {
            const result = await remoteApiPost<IEndUserResponse>('/end_users/password', {
              oldPassword: body.oldPassword,
              newPassword: body.newPassword,
            }, {
              headers: {
                Authorization: `JWT ${auth.token}`,
              },
            });
            if (result.user && result.token) {
              user = result.user;
              token = result.token;
              exp = result.exp;
            }
          } else {
            if (body.oldPassword === '123456Aa!') {
              user = {
                id: 1,
                firstName: 'John',
                lastName: 'Appleseed',
                email: body.email,
                roles: ['guest', 'user'],
                consentPreferences: [],
              };
              token = 'token';
              exp = Math.floor(new Date().getTime() / 1000) + (7 * 24 * 60 * 60);
            }
          }
        }
        const session = await getSession(request, response);
        session.token = token;
        session.exp = exp;
        await session.save();
        if (user) {
          // console.log('api/auth/password', 'success', user);
          response.status(200).json(null);
        } else {
          // console.log('api/auth/password', 'unauthorized');
          response.status(401).json(null);
        }
      } catch (error: any) {
        console.log('api/auth/password', 'error', error.status, error.statusText);
        response.status(401).json(null);
      }
    },
  });
}

export function authUpdateHandler(
  callback?: AuthDecorateCallback
) {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    post: async (request: NextApiRequest, response: NextApiResponse) => {
      const body = request.body;
      if (!body.id) {
        return response.status(400).json({ message: 'Missing Id' });
      }
      // console.log('POST > api/auth/update', body.id);
      let user: IUser | undefined;
      const auth = await getAuth(request, response);
      if (auth.token) {
        if (storeStrategy !== StoreStrategy.Mock) {
          const patch = await remoteApiPatch<IEndUserResponse>(`/end_users/${body.id}`, body, {
            headers: {
              Authorization: `JWT ${auth.token}`,
            },
          });
          const me = await remoteApiGet<IEndUserResponse>('/end_users/me', {
            headers: {
              Authorization: `JWT ${auth.token}`,
            },
          });
          user = me.user || undefined;
        } else {
          user = body;
        }
      }
      if (typeof callback === 'function') {
        user = await callback(user, request, response);
      }
      response.status(200).json(user);
    },
  });
}

export function authForgotHandler({
  Component,
  Layout,
  from = 'no-reply@mixer.com',
  bcc = 'lzampetti@gmail.com',
}: {
  Component: IEmailComponent<ForgotConfirmProps>;
  Layout: IEmailLayoutComponent;
  from?: string; // !!! todo override from app settings in send mail
  bcc?: string;
}) {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    post: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const locale = request.query.locale as (string | undefined);
        const market = request.query.market as (string | undefined);
        if (!market || !locale) {
          return response.status(400).json({ message: 'Missing market or locale' });
        }
        const props: IUserForgot = request.body;
        // console.log('POST > api/auth/forgot', props);
        if (props.checkField !== null) {
          return response.status(401).json({ message: 'Unauthorized' });
        }
        const origin = isDevelopment ? '' : getOrigin(request.headers);
        const layout = await getLayout(market, locale);
        const labelService = new LabelService(layout.labels);
        /**
         * FORGOT
         */
        const subject = labelService.getLabel('email.forgot.subject');
        const html = await renderEmail<ForgotConfirmProps>({
          Component,
          Layout,
          origin,
          layout,
          props: {} as ForgotConfirmProps,
          preview: subject,
          optInUrl: `${layout.topLevelHrefs.reset}?actionId={actionId}`,
          // viewOnWebUrl: `${origin}/api/auth/forgot?market=${layout.market}&locale=${layout.locale}&actionId={actionId}`,
        });
        const emailData: IActionEmailData[] = [{
          to: props.email,
          from,
          bcc,
          subject,
          html,
        }];
        if (storeStrategy !== StoreStrategy.Mock) {
          const action: IUserForgotAction = {
            email: props.email,
            emailData,
          };
          const result = await remoteApiPost<{ code: 'SENT' }>('/end_users/forgot', action);
          // console.log(result);
          response.status(200).json(result);
        } else {
          response.status(200).json(html);
        }
      } catch (error) {
        response.status(400).json(error);
      }
    },
    get: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const locale = request.query.locale as (string | undefined);
        const market = request.query.market as (string | undefined);
        const actionSlug = request.query.actionSlug as (string | undefined);
        let actionId = request.query.actionId as (string | undefined);
        if (!market || !locale) {
          return response.status(400).json({ message: 'Missing market or locale' });
        }
        /*
        if (!actionSlug) {
          return response.status(400).json({ message: 'Missing actionSlug' });
        }
        if (!actionId) {
          return response.status(400).json({ message: 'Missing actionId' });
        }
        */
        const origin = isDevelopment ? '' : getOrigin(request.headers);
        const layout = await getLayout(market, locale);
        const labelService = new LabelService(layout.labels);
        /**
         * FORGOT
         */
        const subject = labelService.getLabel('email.forgot.subject');
        actionId = actionId || '{actionId}';
        const html = await renderEmail<ForgotConfirmProps>({
          Component,
          Layout,
          origin,
          layout,
          props: {} as ForgotConfirmProps,
          preview: subject,
          optInUrl: `${layout.topLevelHrefs.reset}?actionId={actionId}`,
          // viewOnWebUrl: `${origin}/api/auth/forgot?market=${layout.market}&locale=${layout.locale}&actionId={actionId}`,
        });
        // console.log(`${layout.topLevelHrefs.reset}?actionId={actionId}`);
        response.status(200).send(html);
      } catch (error) {
        response.status(400).json(error);
      }
    },
  });
}

export function authResetHandler(
  callback?: AuthDecorateCallback
) {
  return apiHandler({
    options: async (request: NextApiRequest, response: NextApiResponse) => {
      response.status(200).end();
    },
    post: async (request: NextApiRequest, response: NextApiResponse) => {
      try {
        const body = request.body;
        let user: IUser | undefined = undefined;
        let token: string | undefined = undefined;
        let exp: number | undefined = undefined;
        if (storeStrategy !== StoreStrategy.Mock) {
          /*
          const actionSlug = request.body.actionSlug as (string | undefined);
          if (!actionSlug) {
            return response.status(400).json({ message: 'Missing actionSlug' });
          }
          */
          const actionId = request.body.actionId as (string | undefined);
          if (!actionId) {
            return response.status(400).json({ message: 'Missing actionId' });
          }
          const password = request.body.password as (string | undefined);
          if (!password) {
            return response.status(400).json({ message: 'Missing password' });
          }
          const result = await remoteApiPost<IEndUserResponse>('/end_users/reset', {
            actionId,
            password,
          });
          // console.log('loginPost.result', result);
          if (result.user && result.token) {
            /*
            user = Object.fromEntries(
              Object.entries(result.user)
                .filter(kv => ['id', 'email', 'firstName', 'lastName'].includes(kv[0]))
            ) as IUser;
            */
            user = result.user;
            token = result.token;
            exp = result.exp;
          }
        } else {
          if (body.token === 'token' && body.password === '123456Aa!') {
            user = {
              id: 1,
              firstName: 'John',
              lastName: 'Appleseed',
              email: body.email,
              roles: ['guest', 'user'],
              consentPreferences: [],
            };
            token = 'token';
            exp = Math.floor(new Date().getTime() / 1000) + (7 * 24 * 60 * 60);
          }
        }
        if (typeof callback === 'function') {
          user = await callback(user, request, response);
        }
        const session = await getSession(request, response);
        session.token = token;
        session.exp = exp;
        await session.save();
        if (user) {
          // console.log('api/auth/login', 'success', user);
          response.status(200).json(user);
        } else {
          // console.log('api/auth/login', 'unauthorized');
          response.status(401).json(null);
        }
      } catch (error: any) {
        console.log('api/auth/forget/reset', 'error', error.status, error.statusText);
        response.status(401).json(error);
      }
    },
  });
}
