/**
 * profile-svc — личные данные и персонализация пользователя
 * API: GET/PATCH /api/v1/profile
 */

import Fastify from 'fastify';
import cors from '@fastify/cors';
import { eq } from 'drizzle-orm';
import { db } from './db/index.js';
import { userProfiles } from './db/schema.js';

const PORT = parseInt(process.env.PORT ?? '3019', 10);
const AUTH_SERVICE_URL = process.env.AUTH_SERVICE_URL ?? '';

const app = Fastify({ logger: true });

await app.register(cors, { origin: true });

interface AuthUser {
  id: string;
  name?: string;
  email?: string;
  image?: string;
}

async function getAuthUserFromToken(authHeader: string | undefined): Promise<AuthUser | null> {
  if (!authHeader?.startsWith('Bearer ')) return null;
  const token = authHeader.slice(7);
  if (!AUTH_SERVICE_URL) {
    return token ? { id: 'guest' } : null;
  }
  try {
    const res = await fetch(`${AUTH_SERVICE_URL.replace(/\/$/, '')}/api/auth/me`, {
      headers: { Authorization: `Bearer ${token}` },
      signal: AbortSignal.timeout(5000),
    });
    if (!res.ok) return null;
    const data = (await res.json()) as { id?: string; name?: string; email?: string; image?: string };
    return data.id ? { id: data.id, name: data.name, email: data.email, image: data.image } : null;
  } catch {
    return null;
  }
}

app.get('/health', async () => ({ status: 'ok' }));
app.get('/metrics', async (_req, reply) => {
  reply.header('Content-Type', 'text/plain; charset=utf-8');
  return reply.send(
    '# HELP gooseek_up Service is up (1) or down (0)\n# TYPE gooseek_up gauge\ngooseek_up 1\n'
  );
});
app.get('/ready', async () => ({ status: 'ready' }));

/** GET /api/v1/profile — получить профиль текущего пользователя */
app.get('/api/v1/profile', async (req, reply) => {
  const authUser = await getAuthUserFromToken(req.headers.authorization);
  if (!authUser) {
    return reply.status(401).send({ error: 'Authorization required' });
  }
  const userId = authUser.id;
  try {
    let [row] = await db.select().from(userProfiles).where(eq(userProfiles.userId, userId));
    if (!row) {
      try {
        const [inserted] = await db
          .insert(userProfiles)
          .values({
            userId,
            displayName: authUser.name ?? null,
            avatarUrl: authUser.image ?? null,
            timezone: null,
            locale: null,
            profileData: {},
            preferences: {},
            personalization: {},
          })
          .returning();
        row = inserted;
      } catch (err) {
        const [existing] = await db.select().from(userProfiles).where(eq(userProfiles.userId, userId));
        if (existing) row = existing;
        else throw err;
      }
    }
    return reply.send({
      userId: row.userId,
      displayName: row.displayName ?? null,
      avatarUrl: row.avatarUrl ?? null,
      timezone: row.timezone ?? null,
      locale: row.locale ?? null,
      profileData: row.profileData ?? {},
      preferences: row.preferences ?? {},
      personalization: row.personalization ?? {},
      createdAt: row.createdAt?.toISOString?.() ?? row.createdAt,
      updatedAt: row.updatedAt?.toISOString?.() ?? row.updatedAt,
    });
  } catch (err) {
    req.log.error(err);
    return reply.status(500).send({ error: 'Failed to fetch profile' });
  }
});

/** PATCH /api/v1/profile — обновить профиль (merge) */
app.patch<{
  Body: {
    displayName?: string;
    avatarUrl?: string;
    timezone?: string;
    locale?: string;
    profileData?: Record<string, unknown>;
    preferences?: Record<string, unknown>;
    personalization?: Record<string, unknown>;
  };
}>('/api/v1/profile', async (req, reply) => {
  const authUser = await getAuthUserFromToken(req.headers.authorization);
  if (!authUser) {
    return reply.status(401).send({ error: 'Authorization required' });
  }
  const userId = authUser.id;
  const body = req.body ?? {};
  try {
    const [existing] = await db.select().from(userProfiles).where(eq(userProfiles.userId, userId));
    const now = new Date();

    const updates: Record<string, unknown> = {
      updatedAt: now,
    };
    if (body.displayName !== undefined) updates.displayName = body.displayName;
    if (body.avatarUrl !== undefined) updates.avatarUrl = body.avatarUrl;
    if (body.timezone !== undefined) updates.timezone = body.timezone;
    if (body.locale !== undefined) updates.locale = body.locale;

    if (existing) {
      // Merge для вложенных объектов (partial update)
      if (body.profileData !== undefined) {
        updates.profileData = {
          ...(existing.profileData as Record<string, unknown> ?? {}),
          ...body.profileData,
        };
      }
      if (body.preferences !== undefined) {
        updates.preferences = {
          ...(existing.preferences as Record<string, unknown> ?? {}),
          ...body.preferences,
        };
      }
      if (body.personalization !== undefined) {
        updates.personalization = {
          ...(existing.personalization as Record<string, unknown> ?? {}),
          ...body.personalization,
        };
      }

      await db
        .update(userProfiles)
        .set(updates as Record<string, unknown>)
        .where(eq(userProfiles.userId, userId));
    } else {
      await db.insert(userProfiles).values({
        userId,
        displayName: body.displayName ?? null,
        avatarUrl: body.avatarUrl ?? null,
        timezone: body.timezone ?? null,
        locale: body.locale ?? null,
        profileData: body.profileData ?? {},
        preferences: body.preferences ?? {},
        personalization: body.personalization ?? {},
      });
    }

    const [updated] = await db.select().from(userProfiles).where(eq(userProfiles.userId, userId));
    return reply.send({
      userId: updated!.userId,
      displayName: updated!.displayName ?? null,
      avatarUrl: updated!.avatarUrl ?? null,
      timezone: updated!.timezone ?? null,
      locale: updated!.locale ?? null,
      profileData: updated!.profileData ?? {},
      preferences: updated!.preferences ?? {},
      personalization: updated!.personalization ?? {},
      createdAt: updated!.createdAt?.toISOString?.() ?? updated!.createdAt,
      updatedAt: updated!.updatedAt?.toISOString?.() ?? updated!.updatedAt,
    });
  } catch (err) {
    req.log.error(err);
    return reply.status(500).send({ error: 'Failed to update profile' });
  }
});

try {
  await app.listen({ port: PORT, host: '0.0.0.0' });
  console.log('profile-svc listening on :' + PORT);
} catch (err) {
  console.error(err);
  process.exit(1);
}
