import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";
import { UserModel } from "../models/userModel";
import { sendEmail } from "../utils/sendEmail";
import dotenv from "dotenv";
import { TEXT_MESSAGES } from "../const";

dotenv.config({ path: "../.env" });

const JWT_SECRET = process.env.JWT_SECRET || "";
const SALT_ROUNDS = 10;

function cleanUsername(username: string): string {
  return username.replace(TEXT_MESSAGES.USERNAME_REG, "");
}

function mapUserTypeToEnum(
  userType: string
): "SUPERADMIN" | "ADMIN" | "CUSTOMER" {
  switch (userType.toLowerCase()) {
    case "superadmin":
      return "SUPERADMIN";
    case "admin":
      return "ADMIN";
    case "customer":
      return "CUSTOMER";
    default:
      throw new Error(`Invalid userType value: ${userType}`);
  }
}

const userResolvers = {
  Query: {
    me: async (_: any, __: any, { user }: { user: any }) => {
      try {
        if (!user) return null;
        const userData = await UserModel.findByEmail(user.email);
        if (!userData) return null;
        return {
          ...userData,
          userType: mapUserTypeToEnum(userData.userType),
          createdAt: userData.createdAt,
        };
      } catch (error: any) {
        throw new Error(`${TEXT_MESSAGES.FETCH_USER_FAILED} ${error.message}`);
      }
    },

    users: async () => {
      try {
        const users = await UserModel.findAll();
        return users.map((user) => ({
          ...user,
          userType: mapUserTypeToEnum(user.userType),
          createdAt: user.createdAt,
        }));
      } catch (error: any) {
        throw new Error(`${TEXT_MESSAGES.FETCH_USERS_FAILED} ${error.message}`);
      }
    },
    getAllUserDetails: async (
      _: any,
      args: { page?: number; perPage?: number; searchQuery?: string }
    ) => {
      try {
        const { page = 1, perPage = 10, searchQuery = "" } = args || {};
        const [items, totalCount] = await Promise.all([
          UserModel.findAllPaginated({ page, perPage, searchQuery }),
          UserModel.countAll({ searchQuery }),
        ]);

        const mapped = items.map((user: any) => ({
          ...user,
          userType: mapUserTypeToEnum(user.userType),
          createdAt: user.createdAt,
        }));

        const totalPages = Math.max(1, Math.ceil((totalCount || 0) / perPage));
        return { items: mapped, totalCount, totalPages, page, perPage };
      } catch (error: any) {
        throw new Error(`${TEXT_MESSAGES.FETCH_USERS_FAILED} ${error.message}`);
      }
    },
    getCustomer: async (_: any, { id }: { id: number | string }) => {
      try {
        const customer = await UserModel.findById(Number(id));
        if (!customer) throw new Error(TEXT_MESSAGES.USER_NOT_FOUND);
        return {
          ...customer,
          username: cleanUsername(customer.username),
          userType: mapUserTypeToEnum(customer.userType),
          createdAt: customer.createdAt,
        };
      } catch (error: any) {
        throw new Error(`${TEXT_MESSAGES.FETCH_USER_FAILED} ${error.message}`);
      }
    },
    getUserByToken: async (_: any, { token }: { token: string }) => {
      try {
        const decoded: any = jwt.verify(token, process.env.JWT_SECRET!);

        if (!decoded || !decoded.id) {
          throw new Error("Invalid or expired token");
        }

        const user = await UserModel.findById(decoded.id);
        if (!user) {
          throw new Error("User not found");
        }

        return {
          id: user.id,
          email: user.email,
          isApproved: user.isApproved,
          createdAt: user.createdAt,
        };
      } catch (error: any) {
        throw new Error(error.message);
      }
    },
  },

  Mutation: {
    createCustomer: async (
      _: any,
      { input }: { input: { email: string; username: string; password: string; userType?: string } },
      { user }: { user: any }
    ) => {
      try {
        if (!user) throw new Error("Unauthorized");
        const acting = await UserModel.findByEmail(user.email);
        if (!acting || !["admin", "superadmin"].includes((acting.userType || "").toLowerCase())) {
          throw new Error("Forbidden");
        }

        const existingUser = await UserModel.findByEmail(input.email);
        if (existingUser) throw new Error(TEXT_MESSAGES.EMAIL_EXISTS);

        const hashedPassword = await bcrypt.hash(input.password, SALT_ROUNDS);
        const created = await UserModel.createWithUsername(
          input.email,
          input.username,
          hashedPassword,
          (input.userType as any) || "customer"
        );

        // Send welcome + verification email with initial password
        try {
          const verifyToken = jwt.sign({ id: created.id, email: created.email }, JWT_SECRET, { expiresIn: "1h" });
          const verifyLink = `${process.env.FE_URL}emailverification?token=${verifyToken}`;
          const mailOptions = {
            from: "support@intelliwealth.co.uk",
            to: created.email,
            subject: "Welcome to Intelliwealth – Verify Your Email",
            html: `
              <div style="font-family: Arial, sans-serif; color:#222;">
                <h2 style="margin:0 0 12px;">Welcome to Intelliwealth</h2>
                <p style="margin:0 0 12px;">Your account has been created by our team.</p>
                <p style="margin:0 0 12px;"><strong>Username:</strong> ${created.username}<br/>
                <strong>Email:</strong> ${created.email}<br/>
                <strong>Temporary Password:</strong> ${input.password}</p>
                <p style="margin:0 0 16px;">Please verify your email address to activate your account:</p>
                <p style="margin:0 0 20px;">
                  <a href="${verifyLink}" style="display:inline-block;padding:10px 18px;background:#1976d2;color:#fff;text-decoration:none;border-radius:4px;font-weight:bold;">Verify Email</a>
                </p>
                <p style="font-size:13px;color:#555">This link expires in 1 hour. If you did not expect this email, you can ignore it safely.</p>
                <p style="margin-top:16px;">Best regards,<br/>The Intelliwealth Team</p>
              </div>
            `,
          };
          await sendEmail(mailOptions);
        } catch (e) {
          console.error("Failed to send welcome/verification email:", e);
        }

        return {
          ...created,
          userType: mapUserTypeToEnum(created.userType),
          createdAt: created.createdAt,
        };
      } catch (error: any) {
        throw new Error(`Create customer failed: ${error.message}`);
      }
    },

    updateCustomer: async (
      _: any,
      { input }: { input: { id: number | string; username?: string; password?: string; userType?: 'SUPERADMIN'|'ADMIN'|'CUSTOMER' } },
      { user }: { user: any }
    ) => {
      try {
        if (!user) throw new Error("Unauthorized");
        const acting = await UserModel.findByEmail(user.email);
        if (!acting || !["admin", "superadmin"].includes((acting.userType || "").toLowerCase())) {
          throw new Error("Forbidden");
        }

        const userId = Number(input.id);
        const current = await UserModel.findById(userId);
        if (!current) throw new Error(TEXT_MESSAGES.USER_NOT_FOUND);

        if (input.username && input.username.trim()) {
          await UserModel.updateUsername(userId, input.username.trim());
        }
        if (input.password && input.password.trim()) {
          const hashed = await bcrypt.hash(input.password, SALT_ROUNDS);
          await UserModel.updatePassword(userId, hashed);
          // Email the user about password change (include new password if policy allows)
          try {
            const target = await UserModel.findById(userId);
            if (target?.email) {
              const mailOptions = {
                from: "support@intelliwealth.co.uk",
                to: target.email,
                subject: "Your Intelliwealth Password Has Been Updated",
                html: `
                  <div style="font-family: Arial, sans-serif; color:#222;">
                    <h3 style="margin:0 0 12px;">Password Updated</h3>
                    <p style="margin:0 0 12px;">Your password has been updated by an administrator.</p>
                    <p style="margin:0 0 12px;"><strong>New Password:</strong> ${input.password}</p>
                    <p style="margin:0 0 12px;">For security, we recommend changing this password after your next login.</p>
                    <p style="margin-top:16px;">Best regards,<br/>The Intelliwealth Team</p>
                  </div>
                `,
              };
              await sendEmail(mailOptions);
            }
          } catch (e) {
            console.error("Failed to send password update email:", e);
          }
        }
        if (input.userType) {
          const dbType = input.userType.toLowerCase() as 'superadmin'|'admin'|'customer';
          if (!['superadmin','admin','customer'].includes(dbType)) {
            throw new Error('Invalid userType');
          }
          await UserModel.updateUserType(userId, dbType);
        }

        const updated = await UserModel.findById(userId);
        return {
          ...updated,
          username: cleanUsername(updated.username),
          userType: mapUserTypeToEnum(updated.userType),
          createdAt: updated.createdAt,
        };
      } catch (error: any) {
        throw new Error(`Update customer failed: ${error.message}`);
      }
    },

    updateCustomerStatus: async (
      _: any,
      { id, approval }: { id: number | string; approval: boolean },
      { user }: { user: any }
    ) => {
      try {
        if (!user) {
          return { success: false, message: "Unauthorized" };
        }
        const acting = await UserModel.findByEmail(user.email);
        if (!acting || !["admin", "superadmin"].includes((acting.userType || "").toLowerCase())) {
          return { success: false, message: "Forbidden" };
        }

        const userId = Number(id);
        const existing = await UserModel.findById(userId);
        if (!existing) {
          return { success: false, message: TEXT_MESSAGES.USER_NOT_FOUND };
        }

        await UserModel.setApproval(userId, !!approval);
        return { success: true, message: "Customer status updated" };
      } catch (error: any) {
        return { success: false, message: `Failed to update status: ${error.message}` };
      }
    },

    deleteCustomerById: async (
      _: any,
      { id }: { id: number | string },
      { user }: { user: any }
    ) => {
      try {
        if (!user) {
          return { success: false, message: "Unauthorized" };
        }
        const acting = await UserModel.findByEmail(user.email);
        if (!acting || !["admin", "superadmin"].includes((acting.userType || "").toLowerCase())) {
          return { success: false, message: "Forbidden" };
        }
        const userId = Number(id);
        const existing = await UserModel.findById(userId);
        if (!existing) {
          return { success: false, message: TEXT_MESSAGES.USER_NOT_FOUND };
        }
        await UserModel.deleteById(userId);
        return { success: true, message: "Customer deleted successfully" };
      } catch (error: any) {
        return { success: false, message: `Delete customer failed: ${error.message}` };
      }
    },
    registerUser: async (
      _: any,
      { input }: { input: { email: string; password: string } }
    ) => {
      try {
        const existingUser = await UserModel.findByEmail(input.email);
        if (existingUser) throw new Error(TEXT_MESSAGES.EMAIL_EXISTS);
        const hashedPassword = await bcrypt.hash(input.password, SALT_ROUNDS);

        const user = await UserModel.create(
          "Unknown",
          "Unknown",
          input.email,
          hashedPassword,
          null,
          null,
          null,
          null,
          null,
          "customer"
        );

        const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, {
          expiresIn: "8h",
        });

        return {
          token,
          user: {
            ...user,
            userType: mapUserTypeToEnum(user.userType),
            createdAt: user.createdAt,
          },
        };
      } catch (error: any) {
        throw new Error(
          `${TEXT_MESSAGES.REGISTRATION_FAILED} ${error.message}`
        );
      }
    },

    loginUser: async (
      _: any,
      { email, password }: { email: string; password: string }
    ) => {
      try {
        const user = await UserModel.findByEmail(email);
        if (!user) throw new Error(TEXT_MESSAGES.INVALID_CREDENTIALS);

        if (!user.approval) {
          throw new Error("Please verify your email before logging in.");
        }

        const isMatch = await bcrypt.compare(password, user.password);
        if (!isMatch) throw new Error(TEXT_MESSAGES.INVALID_CREDENTIALS);

        // Enforce KYC: Only allow login when latest KYC is Approved (for customers)
        try {
          const latest = await UserModel.findLatestKycStatusByUserId(Number(user.id));
          const status = (latest?.status || "").toString();
          const isCustomer = String(user.userType || "customer").toLowerCase() === "customer";
          if (isCustomer) {
            if (!status || status === "Pending") {
              throw new Error("Your KYC is in process. Please wait a few minutes.");
            }
            if (status === "Rejected" || status === "Cancelled") {
              throw new Error("Your KYC was not approved. Please contact support@intelliwealth.co.uk.");
            }
            // status === Approved -> proceed
          }
        } catch (kycErr: any) {
          // Surface KYC error as login failure message
          throw new Error(`${kycErr?.message || "KYC validation failed"}`);
        }

        const token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, {
          expiresIn: "8h",
        });
        return {
          token,
          user: {
            ...user,
            userType: mapUserTypeToEnum(user.userType),
            createdAt: user.createdAt,
          },
        };
      } catch (error: any) {
        throw new Error(`${TEXT_MESSAGES.LOGIN_FAILED}: ${error.message}`);
      }
    },

    updateProfile: async (_: any, { input }: any) => {
      try {
        const currentUser = await UserModel.findById(Number(input.id));
        if (!currentUser) throw new Error(TEXT_MESSAGES.USER_NOT_FOUND);

        const updatedUser = await UserModel.updateProfile(
          currentUser.id,
          input.firstName || currentUser.firstName,
          input.lastName || currentUser.lastName,
          input.email || currentUser.email,
          input.country || currentUser.country,
          input.address || currentUser.address,
          input.pincode || currentUser.pincode,
          input.companyName || currentUser.companyName,
          input.phoneNumber || currentUser.phoneNumber,
          input.userType || currentUser.userType,
          input.approval || currentUser.approval
        );

        return {
          ...updatedUser,
          username: cleanUsername(updatedUser.username),
          userType: mapUserTypeToEnum(updatedUser.userType),
          createdAt: updatedUser.createdAt,
        };
      } catch (error: any) {
        throw new Error(
          `${TEXT_MESSAGES.UPDATE_PROFILE_FAILED} ${error.message}`
        );
      }
    },

    forgotPassword: async (_: any, { email }: { email: string }) => {
      try {
        const user = await UserModel.findByEmail(email);

        if (!user) {
          return {
            success: false,
            message: TEXT_MESSAGES.EMAIL_NOT_FOUND,
          };
        }

        const resetToken = jwt.sign(
          { id: user.id, email: user.email },
          JWT_SECRET,
          { expiresIn: "1h" }
        );

        const resetLink = `${process.env.FE_URL}change-password?token=${resetToken}`;

        const mailOptions = {
          from: "support@intelliwealth.co.uk",
          to: user.email,
          subject: "Password Reset Request - Intelliwealth",
          html: `
        <p>Hello ${user.firstName || "Customer"},</p>

        <p>We received a request to reset your password for your Intelliwealth account. 
        You can set a new password by clicking the button below:</p>

        <p>
          <a href="${resetLink}" 
             style="display:inline-block; padding:10px 20px; 
                    background-color:#1976d2; color:#ffffff; 
                    text-decoration:none; border-radius:4px; 
                    font-weight:bold;">
            Reset My Password
          </a>
        </p>

        <p>This link will expire in <strong>1 hour</strong>.</p>

        <p>If you did not request this password reset, 
        please ignore this email or contact our support team at 
        <a href="mailto:${process.env.SMTP_USER}">${
            process.env.SMTP_USER
          }</a>.</p>

        <p>Best regards,<br/>The Intelliwealth Team</p>
      `,
        };

        await sendEmail(mailOptions);

        return {
          success: true,
          message: TEXT_MESSAGES.RESET_EMAIL_SENT,
        };
      } catch (error: any) {
        return {
          success: false,
          message: `${TEXT_MESSAGES.RESET_EMAIL_FAILED} ${error.message}`,
        };
      }
    },
    changePassword: async (
      _: any,
      { token, newPassword }: { token: string; newPassword: string }
    ) => {
      try {
        const decoded = jwt.verify(token, JWT_SECRET) as {
          id: number;
          email: string;
        };

        const user = await UserModel.findById(decoded.id);
        if (!user) {
          return {
            success: false,
            message: TEXT_MESSAGES.INVALID_OR_EXPIRED_TOKEN,
          };
        }

        const hashedPassword = await bcrypt.hash(newPassword, SALT_ROUNDS);
        await UserModel.updatePassword(user.id, hashedPassword);

        return {
          success: true,
          message: TEXT_MESSAGES.PASSWORD_SUCCESSFULLY,
        };
      } catch (error: any) {
        return {
          success: false,
          message: `${TEXT_MESSAGES.FAILED_PASSWORD} ${error.message}`,
        };
      }
    },

    changePasswordOnProfile: async (
      _: any,
      { email, newPassword }: { email: string; newPassword: string }
    ) => {
      try {
        const user = await UserModel.findByEmail(email);
        if (!user) {
          return {
            success: false,
            message: TEXT_MESSAGES.USER_NOT_FOUND,
          };
        }

        const hashedPassword = await bcrypt.hash(newPassword, SALT_ROUNDS);
        await UserModel.updatePassword(user.id, hashedPassword);

        return {
          success: true,
          message: TEXT_MESSAGES.PASSWORD_SUCCESSFULLY,
        };
      } catch (error: any) {
        return {
          success: false,
          message: `${TEXT_MESSAGES.FAILED_PASSWORD} ${error.message}`,
        };
      }
    },
    emailVerification: async (_: any, { email }: { email: string }) => {
      try {
        const user = await UserModel.findByEmail(email);

        if (!user) {
          return {
            success: false,
            message: TEXT_MESSAGES.EMAIL_NOT_FOUND,
          };
        }

        const resetToken = jwt.sign(
          { id: user.id, email: user.email },
          JWT_SECRET,
          { expiresIn: "1h" }
        );

        const resetLink = `${process.env.FE_URL}admin/emailverification?token=${resetToken}`;

        const mailOptions = {
          from: "support@intelliwealth.co.uk",
          to: user.email,
          subject: "Please Confirm Your Email Address",
          html: `
  <p>Hello Customer,</p>

  <p>Thank you for signing up with Intelliwealth. To complete your registration, 
  please verify your email address by clicking the link below:</p>

   <p>
          <a href="${resetLink}" 
             style="display:inline-block; padding:10px 20px; 
                    background-color:#1976d2; color:#ffffff; 
                    text-decoration:none; border-radius:4px; 
                    font-weight:bold;">
            Verification Link
          </a>
        </p>

  <p>This link will expire in <strong>1 hour</strong>.</p>

  <p>If you did not request this verification, please ignore this email 
  or contact our support team at <a href="mailto:${process.env.SMTP_USER}">
  ${process.env.SMTP_USER}</a>.</p>

  <p>Best regards,<br/>The Intelliwealth Team</p>
`,
        };

        await sendEmail(mailOptions);

        return {
          success: true,
          message: TEXT_MESSAGES.RESET_EMAIL_SENT,
        };
      } catch (error: any) {
        return {
          success: false,
          message: `${TEXT_MESSAGES.RESET_EMAIL_FAILED} ${error.message}`,
        };
      }
    },
    customerVerification: async (_: any, { email }: { email: string }) => {
      try {
        const user = await UserModel.findByEmail(email);

        if (!user) {
          return {
            success: false,
            message: TEXT_MESSAGES.EMAIL_NOT_FOUND,
          };
        }

        const resetToken = jwt.sign(
          { id: user.id, email: user.email },
          JWT_SECRET,
          { expiresIn: "1h" }
        );

        const resetLink = `${process.env.FE_URL}emailverification?token=${resetToken}`;

        const mailOptions = {
          from: "support@intelliwealth.co.uk",
          to: user.email,
          subject: "Please Confirm Your Email Address",
          html: `
  <p>Hello Customer,</p>

  <p>Thank you for signing up with Intelliwealth. To complete your registration, 
  please verify your email address by clicking the link below:</p>

   <p>
          <a href="${resetLink}" 
             style="display:inline-block; padding:10px 20px; 
                    background-color:#1976d2; color:#ffffff; 
                    text-decoration:none; border-radius:4px; 
                    font-weight:bold;">
             Verification Link
          </a>
        </p>

  <p>This link will expire in <strong>1 hour</strong>.</p>

  <p>If you did not request this verification, please ignore this email 
  or contact our support team at <a href="mailto:${process.env.SMTP_USER}">
  ${process.env.SMTP_USER}</a>.</p>

  <p>Best regards,<br/>The Intelliwealth Team</p>
`,
        };

        await sendEmail(mailOptions);

        return {
          success: true,
          message: TEXT_MESSAGES.RESET_EMAIL_SENT,
        };
      } catch (error: any) {
        return {
          success: false,
          message: `${TEXT_MESSAGES.RESET_EMAIL_FAILED} ${error.message}`,
        };
      }
    },
    emailApproval: async (_: any, { token }: { token: string }) => {
      try {
        const decoded: any = jwt.verify(token, process.env.JWT_SECRET!);

        if (!decoded || !decoded.id) {
          return {
            success: false,
            message: "Invalid or expired token",
          };
        }

        const userId = decoded.id;

        const user = await UserModel.findById(userId);

        if (!user) {
          return {
            success: false,
            message: "User not found",
          };
        }

        await UserModel.approveUser(userId);

        return {
          success: true,
          message: "Email approved successfully",
        };
      } catch (error: any) {
        return {
          success: false,
          message: `Email approval failed: ${error.message}`,
        };
      }
    },
  },
};

export default userResolvers;
