import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import session from "express-session";
import connectPg from "connect-pg-simple";
import bcrypt from "bcryptjs";
import crypto from "crypto";
import nodemailer from "nodemailer";
import type { Express, RequestHandler } from "express";
import { users, systemSettings, type User } from "@shared/models/auth";
import { db } from "./db";
import { eq } from "drizzle-orm";

async function getUser(id: string): Promise<User | undefined> {
  const [user] = await db.select().from(users).where(eq(users.id, id));
  return user;
}

async function getUserByEmail(email: string): Promise<User | undefined> {
  const [user] = await db.select().from(users).where(eq(users.email, email));
  return user;
}

async function createUser(data: {
  email: string;
  password: string;
  firstName: string;
  lastName: string;
  authProvider?: string;
  dateOfBirth?: string;
  userType?: string;
  parentRelation?: string;
}): Promise<User> {
  const hashedPassword = await bcrypt.hash(data.password, 10);
  const verificationToken = crypto.randomBytes(32).toString("hex");
  const [user] = await db
    .insert(users)
    .values({
      email: data.email,
      password: hashedPassword,
      firstName: data.firstName,
      lastName: data.lastName,
      authProvider: data.authProvider || "email",
      emailVerified: false,
      emailVerificationToken: verificationToken,
      dateOfBirth: data.dateOfBirth ? new Date(data.dateOfBirth) : null,
      userType: data.userType || null,
      parentRelation: data.parentRelation || null,
    })
    .returning();
  return user;
}

async function getSmtpTransporter() {
  const host = await getSetting("smtp_host");
  const port = await getSetting("smtp_port");
  const user = await getSetting("smtp_user");
  const pass = await getSetting("smtp_password");
  const fromEmail = await getSetting("smtp_from_email");
  const fromName = await getSetting("smtp_from_name");

  if (!host || !port || !user || !pass) return null;

  const transporter = nodemailer.createTransport({
    host,
    port: parseInt(port),
    secure: parseInt(port) === 465,
    auth: { user, pass },
  });

  return { transporter, fromEmail: fromEmail || user, fromName: fromName || "MADARES Online" };
}

async function sendVerificationEmail(email: string, token: string, baseUrl: string) {
  const smtp = await getSmtpTransporter();
  if (!smtp) return false;

  const verifyUrl = `${baseUrl}/verify-email?token=${token}`;

  await smtp.transporter.sendMail({
    from: `"${smtp.fromName}" <${smtp.fromEmail}>`,
    to: email,
    subject: "تأكيد البريد الإلكتروني - MADARES Online | Email Verification",
    html: `
      <div dir="rtl" style="font-family: 'Cairo', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
        <div style="text-align: center; padding: 20px 0; border-bottom: 2px solid #22c55e;">
          <h1 style="color: #22c55e; margin: 0;">MADARES Online</h1>
          <p style="color: #666; margin: 5px 0;">مدارس أونلاين</p>
        </div>
        <div style="padding: 30px 0;">
          <h2 style="color: #333;">مرحباً بك!</h2>
          <p style="color: #555; line-height: 1.8;">شكراً لتسجيلك في مدارس أونلاين. يرجى تأكيد بريدك الإلكتروني بالنقر على الزر أدناه:</p>
          <div style="text-align: center; margin: 30px 0;">
            <a href="${verifyUrl}" style="background-color: #22c55e; color: white; padding: 14px 40px; border-radius: 8px; text-decoration: none; font-weight: bold; display: inline-block;">تأكيد البريد الإلكتروني</a>
          </div>
          <p style="color: #888; font-size: 13px;">إذا لم تقم بإنشاء هذا الحساب، يمكنك تجاهل هذا البريد.</p>
          <hr style="border: none; border-top: 1px solid #eee; margin: 20px 0;" />
          <p style="color: #555; line-height: 1.8; direction: ltr; text-align: left;">Thank you for registering at MADARES Online. Please verify your email by clicking the button above.</p>
        </div>
      </div>
    `,
  });
  return true;
}

async function upsertOAuthUser(data: {
  email: string;
  firstName: string;
  lastName: string;
  profileImageUrl?: string;
  authProvider: string;
}): Promise<User> {
  const existing = await getUserByEmail(data.email!);
  if (existing) {
    const [user] = await db
      .update(users)
      .set({
        firstName: data.firstName,
        lastName: data.lastName,
        profileImageUrl: data.profileImageUrl,
        updatedAt: new Date(),
      })
      .where(eq(users.id, existing.id))
      .returning();
    return user;
  }
  const [user] = await db
    .insert(users)
    .values({
      email: data.email,
      firstName: data.firstName,
      lastName: data.lastName,
      profileImageUrl: data.profileImageUrl,
      authProvider: data.authProvider,
    })
    .returning();
  return user;
}

export async function getSetting(key: string): Promise<string | null> {
  const [setting] = await db
    .select()
    .from(systemSettings)
    .where(eq(systemSettings.key, key));
  return setting?.value || null;
}

export async function upsertSetting(key: string, value: string): Promise<void> {
  const [existing] = await db
    .select()
    .from(systemSettings)
    .where(eq(systemSettings.key, key));
  if (existing) {
    await db
      .update(systemSettings)
      .set({ value, updatedAt: new Date() })
      .where(eq(systemSettings.key, key));
  } else {
    await db.insert(systemSettings).values({ key, value });
  }
}

export async function getAllSettings(): Promise<Record<string, string>> {
  const settings = await db.select().from(systemSettings);
  const result: Record<string, string> = {};
  for (const s of settings) {
    if (s.value) result[s.key] = s.value;
  }
  return result;
}

export function setupAuth(app: Express) {
  app.set("trust proxy", 1);

  const isProduction = process.env.NODE_ENV === "production";
  const forceHttps = process.env.FORCE_HTTPS === "true";

  if (forceHttps) {
    app.use((req, _res, next) => {
      if (!req.headers["x-forwarded-proto"]) {
        req.headers["x-forwarded-proto"] = "https";
      }
      next();
    });
  }

  const sessionTtl = 7 * 24 * 60 * 60 * 1000;
  const pgStore = connectPg(session);
  const sessionStore = new pgStore({
    conString: process.env.DATABASE_URL,
    createTableIfMissing: true,
    ttl: sessionTtl,
    tableName: "sessions",
  });

  sessionStore.on("error", (error: any) => {
    console.error("Session store error:", error);
  });

  const cookieSecure = process.env.COOKIE_SECURE === "true" || forceHttps;
  const cookieDomain = process.env.COOKIE_DOMAIN || undefined;

  console.log(`[Auth] Session config: secure=${cookieSecure}, domain=${cookieDomain || "auto"}, proxy=true, production=${isProduction}, forceHttps=${forceHttps}`);

  app.use(
    session({
      secret: process.env.SESSION_SECRET || "madares-secret-key-change-in-production",
      store: sessionStore,
      resave: false,
      saveUninitialized: false,
      proxy: true,
      cookie: {
        httpOnly: true,
        secure: cookieSecure,
        sameSite: "lax",
        maxAge: sessionTtl,
        domain: cookieDomain,
      },
    })
  );

  app.use(passport.initialize());
  app.use(passport.session());

  passport.use(
    new LocalStrategy(
      { usernameField: "email", passwordField: "password" },
      async (email, password, done) => {
        try {
          const user = await getUserByEmail(email);
          if (!user) {
            return done(null, false, { message: "البريد الإلكتروني غير مسجل" });
          }
          if (!user.password) {
            return done(null, false, {
              message: "هذا الحساب مسجل عبر Google أو Microsoft. استخدم الطريقة المناسبة",
            });
          }
          const isValid = await bcrypt.compare(password, user.password);
          if (!isValid) {
            return done(null, false, { message: "كلمة المرور غير صحيحة" });
          }
          return done(null, user);
        } catch (error) {
          return done(error);
        }
      }
    )
  );

  passport.serializeUser((user: any, done) => {
    done(null, user.id);
  });

  passport.deserializeUser(async (id: string, done) => {
    try {
      const user = await getUser(id);
      done(null, user || null);
    } catch (error) {
      done(error);
    }
  });

  app.get("/api/auth/registration-config", async (_req, res) => {
    try {
      const ageThreshold = await getSetting("age_threshold_for_role");
      const defaultRole = await getSetting("default_registration_role");
      res.json({
        ageThreshold: parseInt(ageThreshold || "20"),
        defaultRole: defaultRole || "student",
      });
    } catch (error) {
      res.json({ ageThreshold: 20, defaultRole: "student" });
    }
  });

  app.post("/api/auth/register", async (req, res) => {
    try {
      const { email, password, firstName, lastName, dateOfBirth, userType, parentRelation } = req.body;

      if (!email || !password || !firstName || !lastName || !dateOfBirth) {
        return res.status(400).json({ message: "جميع الحقول مطلوبة" });
      }

      if (password.length < 6) {
        return res.status(400).json({ message: "كلمة المرور يجب أن تكون 6 أحرف على الأقل" });
      }

      const birthDate = new Date(dateOfBirth);
      const ageDiff = Date.now() - birthDate.getTime();
      const ageDate = new Date(ageDiff);
      const age = Math.abs(ageDate.getUTCFullYear() - 1970);

      const ageThresholdSetting = await getSetting("age_threshold_for_role");
      const ageThreshold = parseInt(ageThresholdSetting || "20");
      const defaultRoleSetting = await getSetting("default_registration_role");
      const defaultRole = defaultRoleSetting || "student";

      const isAdult = age > ageThreshold;

      if (isAdult && !userType) {
        return res.status(400).json({ message: "يرجى تحديد نوع الحساب (معلم أو ولي أمر)" });
      }

      if (userType === "parent" && !parentRelation) {
        return res.status(400).json({ message: "يرجى تحديد صلة القرابة" });
      }

      const existing = await getUserByEmail(email);
      if (existing) {
        return res.status(400).json({ message: "البريد الإلكتروني مسجل مسبقاً" });
      }

      const user = await createUser({
        email, password, firstName, lastName,
        dateOfBirth,
        userType: isAdult ? userType : "student",
        parentRelation: userType === "parent" ? parentRelation : undefined,
      });

      const role = isAdult ? (userType === "teacher" ? "teacher" : "parent") : defaultRole;

      const { userProfiles } = await import("@shared/schema");
      await db.insert(userProfiles).values({
        userId: user.id,
        role: role as "student" | "teacher" | "parent" | "admin",
        preferredLanguage: "ar",
      }).onConflictDoNothing();

      const baseUrl = `${req.protocol}://${req.get("host")}`;
      const emailSent = await sendVerificationEmail(email, user.emailVerificationToken!, baseUrl);

      const { password: _, emailVerificationToken: __, ...safeUser } = user;
      return res.json({ ...safeUser, emailSent, role });
    } catch (error) {
      console.error("Registration error:", error);
      res.status(500).json({ message: "فشل في إنشاء الحساب" });
    }
  });

  app.get("/api/auth/verify-email", async (req, res) => {
    try {
      const { token } = req.query;
      if (!token) {
        return res.status(400).json({ message: "رمز التحقق مفقود" });
      }

      const [user] = await db.select().from(users).where(eq(users.emailVerificationToken, token as string));
      if (!user) {
        return res.status(400).json({ message: "رمز التحقق غير صالح أو منتهي الصلاحية" });
      }

      if (user.emailVerified) {
        return res.json({ message: "البريد الإلكتروني مؤكد مسبقاً", alreadyVerified: true });
      }

      await db.update(users).set({
        emailVerified: true,
        emailVerificationToken: null,
        updatedAt: new Date(),
      }).where(eq(users.id, user.id));

      return res.json({ message: "تم تأكيد البريد الإلكتروني بنجاح", verified: true });
    } catch (error) {
      console.error("Email verification error:", error);
      res.status(500).json({ message: "فشل في التحقق من البريد الإلكتروني" });
    }
  });

  app.post("/api/auth/resend-verification", async (req, res) => {
    try {
      let userRecord: User | undefined;

      if (req.isAuthenticated() && req.user) {
        userRecord = req.user as User;
      } else if (req.body.email) {
        const [found] = await db.select().from(users).where(eq(users.email, req.body.email));
        userRecord = found;
      }

      if (!userRecord) {
        return res.status(400).json({ message: "User not found" });
      }

      if (userRecord.emailVerified) {
        return res.json({ message: "البريد مؤكد مسبقاً" });
      }

      const newToken = crypto.randomBytes(32).toString("hex");
      await db.update(users).set({
        emailVerificationToken: newToken,
        updatedAt: new Date(),
      }).where(eq(users.id, userRecord.id));

      const baseUrl = `${req.protocol}://${req.get("host")}`;
      const emailSent = await sendVerificationEmail(userRecord.email!, newToken, baseUrl);
      res.json({ message: emailSent ? "تم إرسال رابط التحقق" : "SMTP غير مُعد. تواصل مع المسؤول", emailSent });
    } catch (error) {
      res.status(500).json({ message: "فشل في إعادة إرسال التحقق" });
    }
  });

  app.post("/api/admin/smtp/test", async (req, res) => {
    try {
      if (!req.isAuthenticated()) return res.status(401).json({ message: "Unauthorized" });
      const smtp = await getSmtpTransporter();
      if (!smtp) return res.status(400).json({ message: "إعدادات SMTP غير مكتملة" });
      await smtp.transporter.verify();
      res.json({ success: true, message: "اتصال SMTP ناجح" });
    } catch (error: any) {
      res.status(400).json({ success: false, message: `فشل اتصال SMTP: ${error.message}` });
    }
  });

  app.post("/api/auth/login", (req, res, next) => {
    passport.authenticate("local", (err: any, user: any, info: any) => {
      if (err) {
        console.error("[Auth] Login error:", err);
        return res.status(500).json({ message: "خطأ في الخادم" });
      }
      if (!user) {
        return res.status(401).json({ message: info?.message || "فشل تسجيل الدخول" });
      }
      req.login(user, (loginErr) => {
        if (loginErr) {
          console.error("[Auth] req.login error:", loginErr);
          return res.status(500).json({ message: "فشل في تسجيل الدخول" });
        }
        req.session.save((saveErr) => {
          if (saveErr) {
            console.error("[Auth] Session save error:", saveErr);
            return res.status(500).json({ message: "فشل في حفظ الجلسة" });
          }
          const { password: _, ...safeUser } = user;
          console.log(`[Auth] Login successful for user ${user.email}, session ID: ${req.sessionID}`);
          return res.json(safeUser);
        });
      });
    })(req, res, next);
  });

  app.get("/api/auth/user", (req, res) => {
    if (!req.isAuthenticated() || !req.user) {
      return res.status(401).json({ message: "Unauthorized" });
    }
    const user = req.user as User;
    const { password: _, emailVerificationToken: __, ...safeUser } = user;
    res.json(safeUser);
  });

  app.post("/api/auth/logout", (req, res) => {
    req.logout(() => {
      res.json({ message: "تم تسجيل الخروج" });
    });
  });

  app.get("/api/auth/google/login", async (req, res) => {
    try {
      const clientId = await getSetting("google_client_id");
      if (!clientId) {
        return res.status(400).json({ message: "تسجيل الدخول عبر Google غير مفعل" });
      }
      const redirectUri = `${req.protocol}://${req.get("host")}/api/auth/google/callback`;
      const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=openid%20email%20profile&access_type=offline`;
      res.redirect(authUrl);
    } catch (error) {
      res.status(500).json({ message: "فشل في بدء تسجيل الدخول عبر Google" });
    }
  });

  app.get("/api/auth/google/callback", async (req, res) => {
    try {
      const { code } = req.query;
      if (!code) return res.redirect("/login?error=google_failed");

      const clientId = await getSetting("google_client_id");
      const clientSecret = await getSetting("google_client_secret");
      if (!clientId || !clientSecret) return res.redirect("/login?error=google_not_configured");

      const redirectUri = `${req.protocol}://${req.get("host")}/api/auth/google/callback`;

      const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams({
          code: code as string,
          client_id: clientId,
          client_secret: clientSecret,
          redirect_uri: redirectUri,
          grant_type: "authorization_code",
        }),
      });

      const tokens = await tokenRes.json();
      if (!tokens.access_token) return res.redirect("/login?error=google_failed");

      const profileRes = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
        headers: { Authorization: `Bearer ${tokens.access_token}` },
      });
      const profile = await profileRes.json();

      const user = await upsertOAuthUser({
        email: profile.email,
        firstName: profile.given_name || profile.name?.split(" ")[0] || "",
        lastName: profile.family_name || profile.name?.split(" ").slice(1).join(" ") || "",
        profileImageUrl: profile.picture,
        authProvider: "google",
      });

      req.login(user, (err) => {
        if (err) return res.redirect("/login?error=login_failed");
        req.session.save((saveErr) => {
          if (saveErr) console.error("[Auth] Google session save error:", saveErr);
          res.redirect("/");
        });
      });
    } catch (error) {
      console.error("Google callback error:", error);
      res.redirect("/login?error=google_failed");
    }
  });

  app.get("/api/auth/microsoft/login", async (req, res) => {
    try {
      const clientId = await getSetting("microsoft_client_id");
      const tenantId = await getSetting("microsoft_tenant_id");
      if (!clientId) {
        return res.status(400).json({ message: "تسجيل الدخول عبر Microsoft غير مفعل" });
      }
      const tenant = tenantId || "common";
      const redirectUri = `${req.protocol}://${req.get("host")}/api/auth/microsoft/callback`;
      const authUrl = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=openid%20email%20profile%20User.Read&response_mode=query`;
      res.redirect(authUrl);
    } catch (error) {
      res.status(500).json({ message: "فشل في بدء تسجيل الدخول عبر Microsoft" });
    }
  });

  app.get("/api/auth/microsoft/callback", async (req, res) => {
    try {
      const { code } = req.query;
      if (!code) return res.redirect("/login?error=microsoft_failed");

      const clientId = await getSetting("microsoft_client_id");
      const clientSecret = await getSetting("microsoft_client_secret");
      const tenantId = await getSetting("microsoft_tenant_id");
      if (!clientId || !clientSecret) return res.redirect("/login?error=microsoft_not_configured");

      const tenant = tenantId || "common";
      const redirectUri = `${req.protocol}://${req.get("host")}/api/auth/microsoft/callback`;

      const tokenRes = await fetch(
        `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
        {
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          body: new URLSearchParams({
            code: code as string,
            client_id: clientId,
            client_secret: clientSecret,
            redirect_uri: redirectUri,
            grant_type: "authorization_code",
            scope: "openid email profile User.Read",
          }),
        }
      );

      const tokens = await tokenRes.json();
      if (!tokens.access_token) return res.redirect("/login?error=microsoft_failed");

      const profileRes = await fetch("https://graph.microsoft.com/v1.0/me", {
        headers: { Authorization: `Bearer ${tokens.access_token}` },
      });
      const profile = await profileRes.json();

      const user = await upsertOAuthUser({
        email: profile.mail || profile.userPrincipalName,
        firstName: profile.givenName || profile.displayName?.split(" ")[0] || "",
        lastName: profile.surname || profile.displayName?.split(" ").slice(1).join(" ") || "",
        authProvider: "microsoft",
      });

      req.login(user, (err) => {
        if (err) return res.redirect("/login?error=login_failed");
        req.session.save((saveErr) => {
          if (saveErr) console.error("[Auth] Microsoft session save error:", saveErr);
          res.redirect("/");
        });
      });
    } catch (error) {
      console.error("Microsoft callback error:", error);
      res.redirect("/login?error=microsoft_failed");
    }
  });

  app.get("/api/auth/providers", async (_req, res) => {
    try {
      const googleClientId = await getSetting("google_client_id");
      const microsoftClientId = await getSetting("microsoft_client_id");
      res.json({
        email: true,
        google: !!googleClientId,
        microsoft: !!microsoftClientId,
      });
    } catch (error) {
      res.json({ email: true, google: false, microsoft: false });
    }
  });

}

export const isAuthenticated: RequestHandler = (req, res, next) => {
  if (req.isAuthenticated() && req.user) {
    return next();
  }
  return res.status(401).json({ message: "Unauthorized" });
};
