import fastify from "fastify";
import fastifyCors from "fastify-cors";
import fastifySwagger from "fastify-swagger";

const server = fastify({
  logger: {
    prettyPrint: {
      // TODO: disable in prod
      translateTime: "HH:MM:ss Z",
      ignore: "pid,hostname",
    },
  },
});

server.register(fastifyCors, {
  // TODO: use the correct origins
  origin: "*",
});

server.register(fastifySwagger, {
  exposeRoute: true,
  routePrefix: "/api/doc",
  swagger: {
    info: {
      title: "Ötzi",
      description: "Ötzi app backend API",
      version: "0.1.0",
    },
    host: "localhost:8080",
    schemes: ["http", "https"],
    consumes: ["application/json"],
    produces: ["application/json"],
    tags: [],
    definitions: {},
    // securityDefinitions: {
    //   apiKey: {
    //     type: "apiKey",
    //     name: "apiKey",
    //     in: "header",
    //   },
    // },
  },
});

import apiRoutes from "./api";
server.register(apiRoutes, { prefix: "api" });

import pointOfView from "point-of-view";
import * as ejs from "ejs";
server.register(pointOfView, {
  engine: { ejs: ejs },
  defaultContext: {
    env: "TODO", // TODO: env and tag/sha
  },
});

import { connection } from "./db";

server.get("/", async (request, reply) => {
  // reply.code(200).send("Hello, World!");

  const devicesCount = (await connection.table("devices").count())[0].count;
  const gamesCount = (await connection.table("games").count())[0].count;
  const cluesCount = (await connection.table("clues").count())[0].count;
  const shotsCount = (await connection.table("shots").count())[0].count;

  const normalizedGames = connection
    .table("games")
    .select(
      connection.raw(
        "games.id, games.device_id, games.began_at_gmtm, games.ended_at_gmtm, max(shots.ended_at_gmtm) as last_shot_ended_at_gmtm",
      ),
    )
    .fullOuterJoin("shots", "games.id", "shots.game_id")
    .groupBy("games.id");

  const devicesBehaviour = await connection
    .select(
      connection.raw(
        "sum(coalesce (ended_at_gmtm, last_shot_ended_at_gmtm) - began_at_gmtm) as time_spent, count(case when ended_at_gmtm is not null then 1 end) as ended_count, count(case when ended_at_gmtm is null then 1 end) as interrupted_count, device_id",
      ),
    )
    .from(normalizedGames.as("g"))
    .groupBy("device_id");

  const wordsPerformance = await connection
    .select(
      connection.raw(
        "words.id, words.ocr_transcript, words.ocr_confidence, AVG(shots.similarity) as avg_similarity",
      ),
    )
    .from("shots")
    .join("clues", "clues.id", "shots.clue_id")
    .join("words", "words.id", "clues.word_id")
    .whereNotNull("shots.similarity")
    .groupBy("words.id");

  const devicesByDate = await connection
    .table("games")
    .select(connection.raw("COUNT(DISTINCT device_id), DATE(began_at)"))
    .whereNotNull("device_id")
    .groupByRaw("DATE(began_at)");

  const gamesByDate = await connection
    .table("games")
    .select(
      connection.raw("COUNT(*), DATE(began_at), ended_at IS NOT NULL as ended"),
    )
    .groupByRaw("DATE(began_at), ended");

  const shotsByDuration = await connection
    .table("shots")
    .select(
      connection.raw(
        "width_bucket(ended_at_gmtm - began_at_gmtm, 0, 60*1000, 60*5)*200 as bucket, count(*)",
      ),
    )
    .groupBy("bucket")
    .orderBy("bucket");

  reply.view("/templates/dashboard.ejs", {
    appVersion: process.env.APP_VERSION || "unknown",
    devicesCount,
    gamesCount,
    cluesCount,
    shotsCount,
    devicesByDate,
    gamesByDate,
    shotsByDuration,
    devicesBehaviour,
    wordsPerformance,
  });
});

// TODO: this is an horrible kludge
import fastifyStatic from "fastify-static";
import path from "path";
server.register(fastifyStatic, {
  root: path.join(__dirname, "../public"),
  prefix: "/public/",
});

server.listen(process.env.PORT as string, "0.0.0.0");