import Provider from "oidc-provider";
import express from "express";

const port = process.argv[2] ? Number(process.argv[2]) : 5556;
const cb_url = process.argv[3] ? process.argv[3] : 'http://127.0.0.1:5555/callback';

// ---- Simple in-memory user store ----
const users = [
  {
    id: "1",
    username: "alice",
    password: "password",
    claims: {
      sub: "1",
      email: "alice@example.com",
      email_verified: true,
      preferred_username: "alice",
      name: "Alice",
      groups: ["admin", "dev"],
    },
  },
];

// ---- Account adapter for oidc-provider ----
const Account = {
  async findAccount(ctx, id) {
    const user = users.find((u) => u.id === id);
    if (!user) return undefined;

    return {
      accountId: id,
      async claims(use, scope) {
        console.log(`[IDP] Account.claims called with use=${use} scope=${scope}`);
        return {
          sub: user.claims.sub,
          email: user.claims.email,
          email_verified: user.claims.email_verified,
          preferred_username: user.claims.preferred_username,
          name: user.claims.name,
          groups: user.claims.groups,
        };
      },
    };
  },

  async authenticate(username, password) {
    return users.find(
      (u) => u.username === username && u.password === password
    );
  },
};

// ---- OIDC provider configuration ----
const configuration = {
  clients: [{
    client_id: 'zot-client',
    client_secret: 'ZXhhbXBsZS1hcHAtc2VjcmV0',
    redirect_uris: [cb_url], // Will be overridden dynamically
    response_types: ["code"],
    grant_types: ["authorization_code"],
    token_endpoint_auth_method: "client_secret_basic",
    scope: "openid profile email groups",
  }],

  features: {
    devInteractions: { enabled: false }, // we provide our own login
    introspection: { enabled: true },
    revocation: { enabled: true },
    resourceIndicators: { enabled: false },
    // claimsParameter is now automatically supported
  },

  interactions: {
    url(ctx, interaction) {
      return `/login?uid=${interaction.uid}`;
    },
  },

  async findAccount(ctx, id) {
    return Account.findAccount(ctx, id);
  },

  // Required for ZITADEL: return standard claims
  scopes: ["openid", "profile", "email", "groups"],
  claims: {
    openid: ["sub"],
    profile: ["name", "preferred_username"],
    email: ["email", "email_verified"],
    groups: ["groups"],
  },

  async renderError(ctx, out, error) {
    ctx.body = `OIDC Error: ${error.error_description}`;
  },
};

// ---- Create provider ----
const issuer = `http://127.0.0.1:${port}/`;
const provider = new Provider(issuer, configuration);

// Monkey-patch Client prototype to allow dynamic redirect URIs
const Client = provider.Client;
const originalRedirectUriAllowed = Client.prototype.redirectUriAllowed;

Client.prototype.redirectUriAllowed = function(value) {
  // Allow any localhost URI for testing
  try {
    const parsed = new URL(value);
    if (parsed.hostname === '127.0.0.1' && parsed.pathname === '/zot/auth/callback/oidc') {
      return true;
    }
  } catch (e) {
    // ignore invalid URLs
  }
  return originalRedirectUriAllowed.call(this, value);
};

provider.Client.find('zot-client').then(client => {
  console.log('Startup check: Client found:', client ? 'yes' : 'no');
}).catch(err => console.error('Startup check error:', err));

// ---- Simple login UI ----
const app = express();
app.use(express.urlencoded({ extended: true }));

app.use((req, res, next) => {
  console.log(`[IDP] Request: ${req.method} ${req.url}`);
  next();
});

app.get("/login", async (req, res) => {
  console.log("Hit /login endpoint");
  const { uid } = req.query;
  const user = users[0];

  const result = await provider.interactionDetails(req, res);
  console.log("Interaction details:", result);
  if (result.prompt.details) {
    console.log("Prompt details:", JSON.stringify(result.prompt.details, null, 2));
  }

  if (result.prompt.name === 'login') {
    await provider.interactionFinished(
      req,
      res,
      {
        login: {
          accountId: user.id,
        },
      },
      { mergeWithLastSubmission: false }
    );
  } else {
    // Manually create a grant to ensure scopes are accepted
    const grant = new provider.Grant({
      accountId: result.session.accountId,
      clientId: result.params.client_id,
    });
    
    const scopes = result.params.scope.split(' ');
    console.log(`[IDP] Manually granting scopes: ${scopes.join(' ')}`);
    grant.addOIDCScope(scopes.join(' '));
    
    const grantId = await grant.save();
    
    await provider.interactionFinished(
      req,
      res,
      { consent: { grantId } },
      { mergeWithLastSubmission: true }
    );
  }
  console.log("Interaction finished");
});

// app.post("/login", async (req, res, next) => { ... });

// ---- mount OIDC provider ----
app.use(provider.callback());
app.listen(port, () => {
  console.log(`OIDC Provider listening at ${issuer}`);
});
