import { z } from "zod";
import { protectedProcedure, router } from "../_core/trpc.ts";
import * as db from "../db.ts";
import { storagePut } from "../storage.ts";
import { TRPCError } from "@trpc/server";

export const profileRouter = router({
  /**
   * Get current user's profile
   */
  getProfile: protectedProcedure.query(async ({ ctx }) => {
    const user = await db.getUserById(ctx.user.id);
    if (!user) {
      throw new TRPCError({
        code: "NOT_FOUND",
        message: "User not found",
      });
    }

    return {
      id: user.id,
      name: user.name,
      email: user.email,
      avatarUrl: user.avatarUrl,
      socialProvider: user.socialProvider,
      socialId: user.socialId,
      socialProfileData: user.socialProfileData
        ? JSON.parse(user.socialProfileData as string)
        : null,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
    };
  }),

  /**
   * Update user profile
   */
  updateProfile: protectedProcedure
    .input(
      z.object({
        name: z.string().min(1).max(64).optional(),
        avatarUrl: z.string().url().optional(),
        bio: z.string().max(256).optional(),
        overrideSocialData: z.boolean().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const user = await db.getUserById(ctx.user.id);
      if (!user) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "User not found",
        });
      }

      // Prepare update data
      const updateData: any = {};

      if (input.name) {
        updateData.name = input.name;
      }

      if (input.avatarUrl) {
        updateData.avatarUrl = input.avatarUrl;
      }

      // Update profile metadata
      if (input.bio !== undefined || input.overrideSocialData) {
        const currentData = user.socialProfileData
          ? JSON.parse(user.socialProfileData as string)
          : {};

        const newData = {
          ...currentData,
          bio: input.bio || currentData.bio,
          overrideSocialData: input.overrideSocialData || false,
          lastEditedAt: new Date().toISOString(),
        };

        updateData.socialProfileData = JSON.stringify(newData);
      }

      // Update user in database
      const updated = await db.upsertUser({
        openId: user.openId,
        ...updateData,
      });

      return {
        id: updated.id,
        name: updated.name,
        email: updated.email,
        avatarUrl: updated.avatarUrl,
        socialProvider: updated.socialProvider,
        socialProfileData: updated.socialProfileData
          ? JSON.parse(updated.socialProfileData as string)
          : null,
      };
    }),

  /**
   * Get linked accounts for current user
   */
  getLinkedAccounts: protectedProcedure.query(async ({ ctx }) => {
    const linkedAccounts = await db.getLinkedAccounts(ctx.user.id);
    return linkedAccounts.map((account) => ({
      provider: account.provider,
      email: account.email,
      displayName: account.displayName,
      linkedAt: account.linkedAt,
      isPrimary: false,
    }));
  }),

  /**
   * Link a new social account
   */
  linkAccount: protectedProcedure
    .input(
      z.object({
        provider: z.enum(["google", "github", "apple"]),
        socialId: z.string(),
        email: z.string().email().optional(),
        displayName: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const user = await db.getUserById(ctx.user.id);
      if (!user) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "User not found",
        });
      }

      // Check if this social account is already linked to another user
      const existingUser = await db.getUserBySocialId(
        input.socialId,
        input.provider
      );
      if (existingUser && existingUser.id !== ctx.user.id) {
        throw new TRPCError({
          code: "CONFLICT",
          message: "This social account is already linked to another user",
        });
      }

      // Store linked account in database
      await db.addLinkedAccount({
        userId: ctx.user.id,
        provider: input.provider,
        socialId: input.socialId,
        email: input.email,
        displayName: input.displayName,
        linkedAt: new Date(),
      });

      return {
        provider: input.provider,
        email: input.email,
        displayName: input.displayName,
        linkedAt: new Date().toISOString(),
      };
    }),

  /**
   * Unlink a social account
   */
  unlinkAccount: protectedProcedure
    .input(
      z.object({
        provider: z.enum(["google", "github", "apple"]),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const user = await db.getUserById(ctx.user.id);
      if (!user) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "User not found",
        });
      }

      // Check if user has at least one other linked account
      const linkedAccounts = await db.getLinkedAccounts(ctx.user.id);
      if (linkedAccounts.length <= 1) {
        throw new TRPCError({
          code: "FORBIDDEN",
          message: "You must keep at least one account linked",
        });
      }

      // Remove linked account
      await db.removeLinkedAccount(ctx.user.id, input.provider);

      // If this was the primary provider, update it
      if (user.socialProvider === input.provider) {
        const remainingAccounts = linkedAccounts.filter(
          (a) => a.provider !== input.provider
        );
        if (remainingAccounts.length > 0) {
          await db.upsertUser({
            openId: user.openId,
            socialProvider: remainingAccounts[0].provider,
            socialId: remainingAccounts[0].socialId,
          });
        }
      }

      return {
        success: true,
        message: `${input.provider} account unlinked successfully`,
      };
    }),

  /**
   * Upload avatar image
   */
  uploadAvatar: protectedProcedure
    .input(
      z.object({
        fileData: z.string(), // base64 encoded file data
        fileName: z.string(),
        mimeType: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      try {
        // Convert base64 to buffer
        const buffer = Buffer.from(input.fileData, "base64");

        // Generate unique filename
        const timestamp = Date.now();
        const filename = `avatars/user_${ctx.user.id}_${timestamp}_${input.fileName}`;

        // Upload to S3
        const { url } = await storagePut(filename, buffer, input.mimeType);

        // Update user avatar URL
        await db.upsertUser({
          openId: (await db.getUserById(ctx.user.id))?.openId || "",
          avatarUrl: url,
        });

        return {
          url,
          fileName: filename,
        };
      } catch (error) {
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Failed to upload avatar",
        });
      }
    }),
});
