import type { FastifyInstance } from "fastify";
import type { MultipartFile } from "@fastify/multipart";
import { z } from "zod";

import { AppError } from "../lib/errors.js";
import { requireProfile } from "../services/auth-service.js";
import { supabaseAdmin } from "../lib/supabase.js";
import { isoNow } from "../lib/utils.js";

function toFullName(parts: { firstName?: string; lastName?: string; fullName?: string }) {
  return parts.fullName?.trim() || [parts.firstName, parts.lastName].filter(Boolean).join(" ").trim();
}

async function toBuffer(file: MultipartFile) {
  const chunks: Buffer[] = [];

  for await (const chunk of file.file) {
    chunks.push(Buffer.from(chunk));
  }

  return Buffer.concat(chunks);
}

export function registerAccountRoutes(app: FastifyInstance) {
  app.get("/api/account/me", async (request) => {
    const { user, profile } = await requireProfile(request);

    const { data: organizations, error } = await supabaseAdmin
      .from("organization_members")
      .select("role, organizations(id, name, slug)")
      .eq("auth_user_id", user.id);

    if (error) {
      throw new AppError(400, "ACCOUNT_LOOKUP_FAILED", error.message);
    }

    const avatarUrl =
      profile.avatar_path
        ? (
            await supabaseAdmin.storage
              .from("user-avatars")
              .createSignedUrl(String(profile.avatar_path), 60 * 60)
          ).data?.signedUrl ?? null
        : null;

    return {
      user: {
        ...user,
        active_organization_id: request.authContext.organizationId ?? profile.default_organization_id ?? null
      },
      profile: {
        ...profile,
        avatar_url: avatarUrl
      },
      organizations: ((organizations ?? []) as Array<Record<string, any>>).map((entry) => ({
        role: entry.role,
        organization: Array.isArray(entry.organizations) ? entry.organizations[0] : entry.organizations
      }))
    };
  });

  app.put("/api/account", async (request) => {
    const { user, profile } = await requireProfile(request);
    let fullName = typeof profile.full_name === "string" ? profile.full_name : "";
    let avatarPath: string | null = typeof profile.avatar_path === "string" ? profile.avatar_path : null;
    let firstName: string | undefined;
    let lastName: string | undefined;

    if (request.isMultipart()) {
      const parts = request.parts();

      for await (const part of parts) {
        if (part.type === "file") {
          const buffer = await toBuffer(part);
          avatarPath = `users/${user.id}/avatar/${Date.now()}-${part.filename}`;
          const upload = await supabaseAdmin.storage.from("user-avatars").upload(avatarPath, buffer, {
            contentType: part.mimetype,
            upsert: true
          });

          if (upload.error) {
            throw new AppError(400, "ACCOUNT_AVATAR_UPLOAD_FAILED", upload.error.message);
          }
        } else if (part.fieldname === "fullName") {
          fullName = String(part.value ?? "").trim() || fullName;
        } else if (part.fieldname === "firstName") {
          firstName = String(part.value ?? "").trim();
        } else if (part.fieldname === "lastName") {
          lastName = String(part.value ?? "").trim();
        }
      }

      const nextFullName = toFullName({ firstName, lastName, fullName });
      if (nextFullName) fullName = nextFullName;
    } else {
      const payload = z
        .object({
          fullName: z.string().trim().min(2).optional(),
          firstName: z.string().trim().optional(),
          lastName: z.string().trim().optional()
        })
        .transform(({ firstName, lastName, fullName }) => ({ fullName: toFullName({ firstName, lastName, fullName }) }))
        .refine((value) => value.fullName.length >= 2, {
          message: "A valid name is required.",
          path: ["fullName"]
        })
        .parse(request.body);
      fullName = payload.fullName;
    }

    const { data, error } = await supabaseAdmin
      .from("user_profiles")
      .update({
        full_name: fullName,
        avatar_path: avatarPath,
        updated_at: isoNow()
      })
      .eq("auth_user_id", user.id)
      .select("*")
      .single();

    if (error || !data) {
      throw new AppError(400, "ACCOUNT_UPDATE_FAILED", error?.message ?? "Unable to update account.");
    }

    const avatarUrl =
      data.avatar_path
        ? (
            await supabaseAdmin.storage
              .from("user-avatars")
              .createSignedUrl(String(data.avatar_path), 60 * 60)
          ).data?.signedUrl ?? null
        : null;

    return {
      profile: {
        ...profile,
        full_name: data.full_name,
        avatar_path: data.avatar_path,
        avatar_url: avatarUrl
      }
    };
  });

  app.post("/api/account/onboarding", async (request) => {
    const { user } = await requireProfile(request);
    const payload = z
      .object({
        completed: z.boolean().default(true)
      })
      .parse(request.body ?? {});

    const { data, error } = await supabaseAdmin
      .from("user_profiles")
      .update({
        onboarding_completed_at: payload.completed ? isoNow() : null,
        updated_at: isoNow()
      })
      .eq("auth_user_id", user.id)
      .select("onboarding_completed_at")
      .single();

    if (error || !data) {
      throw new AppError(400, "ACCOUNT_ONBOARDING_UPDATE_FAILED", error?.message ?? "Unable to update onboarding state.");
    }

    return {
      profile: {
        onboarding_completed_at: data.onboarding_completed_at
      }
    };
  });
}
