import { USERS } from './../../db/resources';
import { JWT, LoginForm, User } from './../../types/schemaTypes';
import { respondInternalError, respondUnauthorized } from './../../utils/responses';
import { RouteFunction } from './../../types/index';
import swaggerSchemas from '../../db/swaggerSchemas';
import { validateSchemaOnRuntime } from '../../utils/';
import { LoginFormSchema } from '../../db/schemas/UserSchema';
import { OAuth2Client, TokenPayload } from 'google-auth-library';
import { generateAccessToken } from '../../utils/generateAccessToken';
import dbo from '../../db/conn';
import { AUTH_EXPIRE_TIME } from '../../config/authentication';

const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

// swagger info
export const swPostLogin = {
  summary: 'Login to the webpage',
  tags: ['Login'],
  requestBody: {
    content: {
      'application/json': {
        schema: {
          ...swaggerSchemas.LoginFormSchema,
        },
      },
    },
  },
  responses: {
    '200': {
      description: 'Returns JWT Token object',
      content: {
        'application/json': {
          schema: {
            ...swaggerSchemas.JWTSchema,
          },
        },
      },
    },
    default: {
      description: 'Error message',
    },
  },
};

type IverifyTokenFn = (idToken: string) => Promise<TokenPayload | undefined>;

const verifyIdToken: IverifyTokenFn = async (idToken) => {
  const ticket = await client.verifyIdToken({
    idToken,
    audience: process.env.GOOGLE_CLIENT_ID
  });
  const payload = ticket.getPayload();
  return payload;
};

const OneOfLoginFormSchema = LoginFormSchema.refine(data => data.googleOAuth || data.basicAuth, 'googleOAuth: { idToken, clientId } or basicAuth: { username, password } is required.');

export const login: RouteFunction = async (req, res) => {
  const { body } = req;
  const validatedData = await validateSchemaOnRuntime<LoginForm>(OneOfLoginFormSchema, body, res);
  if (!validatedData) return;
  try {
    const db = dbo.getDb();
    const usersCollection = db.collection(USERS);
    const { googleOAuth } = validatedData;
    if (googleOAuth) {
      try {
        // verify if the info is of a valid Google account
        const payload = await verifyIdToken(googleOAuth.idToken);
        if (payload) {
          // user is a valid google account, look for registered user
          const userEmail = payload.email;
          const findResult = await usersCollection.findOne<User>({
            userId: userEmail,
          });
          if (findResult) {
            const token = generateAccessToken(payload?.sub);
            // found user, send token back to client
            const jwtResponse: JWT = {
              access_token: token,
              type: 'Bearer',
              expire_at: new Date().getTime() + (AUTH_EXPIRE_TIME * 1000)
            };
            res.send(jwtResponse);
            return;
          }
        }
        respondUnauthorized(res, 'User is not registered.');
      } catch (e) {
        console.log('Error', e);
        respondUnauthorized(res, e);
      }
    }
  } catch (err) {
    console.log('Error', err);
    respondInternalError(res, err);
  }
};
