import bcrypt from "bcryptjs";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z } from "zod";
import { config } from "./config.js";
import { db } from "./db.js";
import { AuthUser, UserRow } from "./types.js";

declare module "fastify" {
  interface FastifyRequest {
    userInfo: AuthUser;
  }
}

const registerSchema = z.object({
  username: z.string().min(3).max(32),
  password: z.string().min(6).max(128),
  captcha: z.string().optional()
});

const loginSchema = registerSchema.pick({
  username: true,
  password: true
}).extend({
  captcha: z.string().optional()
});

const userCreateSchema = z.object({
  username: z.string().min(3).max(32),
  password: z.string().min(6).max(128),
  role: z.enum(["admin", "user"]).default("user"),
  enabled: z.boolean().default(true),
  expiresAt: z.string().nullable().optional()
});

const userUpdateSchema = z.object({
  role: z.enum(["admin", "user"]),
  enabled: z.boolean(),
  expiresAt: z.string().nullable().optional(),
  password: z.string().min(6).max(128).optional()
});

function publicUser(row: UserRow) {
  return {
    id: row.id,
    username: row.username,
    role: row.role,
    enabled: Boolean(row.enabled),
    expires_at: row.expires_at,
    created_at: row.created_at,
    updated_at: row.updated_at
  };
}

function normalizeExpiresAt(value: string | null | undefined) {
  const clean = value?.trim();
  if (!clean) return null;
  const date = new Date(clean);
  if (Number.isNaN(date.getTime())) {
    throw new Error("授权到期时间格式不正确");
  }
  return clean.length === 10 ? `${clean}T23:59:59` : clean;
}

async function ensureFixedLoginUser() {
  const username = config.fixedLogin.username.trim() || "viewer";
  const existing = db.prepare(`
    SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
    FROM users
    WHERE username = ?
  `).get(username) as unknown as UserRow | undefined;
  if (existing) {
    db.prepare(`
      UPDATE users
      SET role = ?, enabled = 1, expires_at = NULL, updated_at = CURRENT_TIMESTAMP
      WHERE id = ?
    `).run(config.fixedLogin.role, existing.id);
    return db.prepare(`
      SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
      FROM users
      WHERE id = ?
    `).get(existing.id) as unknown as UserRow;
  }

  const passwordHash = await bcrypt.hash(`fixed-${Date.now()}-${Math.random()}`, 10);
  const result = db.prepare(`
    INSERT INTO users (username, password_hash, role, enabled, expires_at)
    VALUES (?, ?, ?, 1, NULL)
  `).run(username, passwordHash, config.fixedLogin.role);
  return db.prepare(`
    SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
    FROM users
    WHERE id = ?
  `).get(Number(result.lastInsertRowid)) as unknown as UserRow;
}

function authUser(row: Pick<UserRow, "id" | "username" | "role">): AuthUser {
  return {
    id: row.id,
    username: row.username,
    role: row.role
  };
}

export async function authRoutes(app: FastifyInstance) {
  app.get("/api/auth/bootstrap", async () => {
    if (config.authMode !== "fixed") {
      return { mode: "login" };
    }
    const user = await ensureFixedLoginUser();
    const payload = authUser(user);
    const token = app.jwt.sign(payload);
    return { mode: "fixed", token, user: payload };
  });

  app.post("/api/auth/register", async (request, reply) => {
    const body = registerSchema.parse(request.body);
    const passwordHash = await bcrypt.hash(body.password, 10);
    try {
      db.prepare("INSERT INTO users (username, password_hash, role, enabled) VALUES (?, ?, 'user', 1)")
        .run(body.username, passwordHash);
      return reply.code(201).send({ ok: true });
    } catch {
      return reply.code(409).send({ message: "用户名已存在" });
    }
  });

  app.post("/api/auth/login", async (request, reply) => {
    const body = loginSchema.parse(request.body);
    const user = db.prepare(`
      SELECT id, username, password_hash, role, enabled, expires_at
      FROM users
      WHERE username = ?
    `).get(body.username) as {
      id: number;
      username: string;
      password_hash: string;
      role: "admin" | "user";
      enabled: number;
      expires_at: string | null;
    } | undefined;

    if (!user || !(await bcrypt.compare(body.password, user.password_hash))) {
      return reply.code(401).send({ message: "用户名或密码错误" });
    }
    if (!user.enabled) {
      return reply.code(403).send({ message: "账号已禁用" });
    }
    if (user.expires_at && new Date(user.expires_at).getTime() < Date.now()) {
      return reply.code(403).send({ message: "账号授权已过期" });
    }

    const payload = authUser(user);
    const token = app.jwt.sign(payload);
    return { token, user: payload };
  });

  app.get("/api/auth/me", { preHandler: [requireAuth] }, async (request) => {
    return { user: request.userInfo };
  });

  app.get("/api/users", { preHandler: [requireAdmin] }, async () => {
    const rows = db.prepare(`
      SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
      FROM users
      ORDER BY id ASC
    `).all() as unknown as UserRow[];
    return { users: rows.map(publicUser) };
  });

  app.post("/api/users", { preHandler: [requireAdmin] }, async (request, reply) => {
    const body = userCreateSchema.parse(request.body);
    const passwordHash = await bcrypt.hash(body.password, 10);
    try {
      const result = db.prepare(`
        INSERT INTO users (username, password_hash, role, enabled, expires_at)
        VALUES (?, ?, ?, ?, ?)
      `).run(body.username.trim(), passwordHash, body.role, body.enabled ? 1 : 0, normalizeExpiresAt(body.expiresAt));
      const row = db.prepare(`
        SELECT id, username, password_hash, role, enabled, expires_at, created_at, updated_at
        FROM users
        WHERE id = ?
      `).get(Number(result.lastInsertRowid)) as unknown as UserRow;
      return reply.code(201).send({ user: publicUser(row) });
    } catch (error) {
      if (error instanceof Error && error.message.includes("授权到期时间")) {
        return reply.code(400).send({ message: error.message });
      }
      return reply.code(409).send({ message: "用户名已存在" });
    }
  });

  app.put("/api/users/:id", { preHandler: [requireAdmin] }, async (request, reply) => {
    const params = z.object({ id: z.coerce.number().int() }).parse(request.params);
    const body = userUpdateSchema.parse(request.body);
    const user = db.prepare("SELECT * FROM users WHERE id = ?").get(params.id) as UserRow | undefined;
    if (!user) {
      return reply.code(404).send({ message: "用户不存在" });
    }
    if (user.id === request.userInfo.id && (!body.enabled || body.role !== "admin")) {
      return reply.code(400).send({ message: "不能取消当前登录管理员的权限" });
    }
    const adminCount = db.prepare("SELECT COUNT(*) AS count FROM users WHERE role = 'admin' AND enabled = 1")
      .get() as { count: number };
    if (user.role === "admin" && user.enabled && (body.role !== "admin" || !body.enabled) && adminCount.count <= 1) {
      return reply.code(400).send({ message: "至少保留一个启用的管理员" });
    }
    try {
      const expiresAt = normalizeExpiresAt(body.expiresAt);
      if (body.password) {
        const passwordHash = await bcrypt.hash(body.password, 10);
        db.prepare(`
          UPDATE users
          SET role = ?, enabled = ?, expires_at = ?, password_hash = ?, updated_at = CURRENT_TIMESTAMP
          WHERE id = ?
        `).run(body.role, body.enabled ? 1 : 0, expiresAt, passwordHash, user.id);
      } else {
        db.prepare(`
          UPDATE users
          SET role = ?, enabled = ?, expires_at = ?, updated_at = CURRENT_TIMESTAMP
          WHERE id = ?
        `).run(body.role, body.enabled ? 1 : 0, expiresAt, user.id);
      }
      return { ok: true };
    } catch (error) {
      const message = error instanceof Error ? error.message : "保存失败";
      return reply.code(400).send({ message });
    }
  });

  app.delete("/api/users/:id", { preHandler: [requireAdmin] }, async (request, reply) => {
    const params = z.object({ id: z.coerce.number().int() }).parse(request.params);
    if (params.id === request.userInfo.id) {
      return reply.code(400).send({ message: "不能删除当前登录用户" });
    }
    const user = db.prepare("SELECT * FROM users WHERE id = ?").get(params.id) as UserRow | undefined;
    if (!user) {
      return reply.code(404).send({ message: "用户不存在" });
    }
    const adminCount = db.prepare("SELECT COUNT(*) AS count FROM users WHERE role = 'admin' AND enabled = 1")
      .get() as { count: number };
    if (user.role === "admin" && user.enabled && adminCount.count <= 1) {
      return reply.code(400).send({ message: "至少保留一个启用的管理员" });
    }
    db.prepare("DELETE FROM users WHERE id = ?").run(user.id);
    return { ok: true };
  });
}

export async function requireAuth(request: FastifyRequest, reply: FastifyReply) {
  try {
    const payload = await request.jwtVerify<AuthUser>();
    const user = db.prepare("SELECT id, username, role, enabled, expires_at FROM users WHERE id = ?")
      .get(payload.id) as Pick<UserRow, "id" | "username" | "role" | "enabled" | "expires_at"> | undefined;
    if (!user) {
      return reply.code(401).send({ message: "请先登录" });
    }
    if (!user.enabled) {
      return reply.code(403).send({ message: "账号已禁用" });
    }
    if (user.expires_at && new Date(user.expires_at).getTime() < Date.now()) {
      return reply.code(403).send({ message: "账号授权已过期" });
    }
    request.userInfo = {
      id: user.id,
      username: user.username,
      role: user.role
    };
  } catch {
    return reply.code(401).send({ message: "请先登录" });
  }
}

export async function requireAdmin(request: FastifyRequest, reply: FastifyReply) {
  await requireAuth(request, reply);
  if (reply.sent) return;
  if (request.userInfo.role !== "admin") {
    return reply.code(403).send({ message: "需要管理员权限" });
  }
}
