package com.oguerreiro.resilient.security.saml2;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;

/**
 * In a DEV environment a mock-idp might be needed for SAMLv2 authentication. This will automatically run the mock-idp
 * daemon for testing.<br>
 * The mock-idp is a separate NodeJS project in : src\main\mock-idp. More info about this in the readme file, inside the
 * project dir.<br>
 * Sometimes it can be useful to run MOCK-IDP separately:
 * <ol>
 * <li>disable it in application.yml ({@code resilient.mock-idp. enabled: true})</li>
 * <li>open a command line console</li>
 * <li>goto the mock-idp dir</li>
 * <li>execute: {@code node idp.js}</li>
 * </ol>
 * <br>
 * To run this, configuration must be added to application<-dev>.yml:
 * 
 * <pre>
 * <code>
spring:
  security:
    saml2:     # ADDED to support SAMLv2 authentication to IDP.
      relyingparty:
        registration:
          mock-idp:
            assertingparty:
              entity-id: http://localhost:3000/saml/metadata
              single-sign-on:
                url: http://localhost:3000/saml/sso
              single-logout:
                url: http://localhost:3000/saml/slo
              verification:
                credentials:
                  - certificate-location: classpath:saml/idp-public.cert
              want-authn-signed: false    # Validate signature in entire message response (true-validates/false-doesn't validate)
              want-assertion-signed: true # Validate signature in assertions message response (true-validates/false-doesn't validate)
            signing:
              credentials:
                - private-key-location: classpath:saml/private.key
                  certificate-location: classpath:saml/public.cert
resilient:
  mock-idp:
    enabled: true
    path: classpath:mock-idp/idp.js
 * </code>
 * </pre>
 */
@Component
@Profile("dev")
public class DevMockIdpStarter {

  @Value("${resilient.mock-idp.enabled:false}")
  private Boolean mockIdpEnabled;

  @Value("${resilient.mock-idp.path:}") //Optional path. 
  private String mockIdpPath;

  @Autowired
  private ResourceLoader resourceLoader;

  @PostConstruct
  public void startMockIdp() throws IOException {
    // If not set, ignore and DON'T run the SAML2 mock-idp
    if (!mockIdpEnabled || mockIdpPath == null || mockIdpPath.isBlank())
      return;

    String realMockIdpPath = getRealMockIdpPath();

    ProcessBuilder builder = new ProcessBuilder("node", "--trace-warnings", realMockIdpPath);
    builder.inheritIO(); // Optional: pipes output to Spring console

    Path path = Paths.get(realMockIdpPath);
    Path realMockIdpPathParent = path.getParent();
    builder.directory(realMockIdpPathParent.toFile()); // working directory

    Process process = builder.start();

    // Optional: add a shutdown hook to stop it on JVM shutdown
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
      process.destroy();
    }));
  }

  private String getRealMockIdpPath() throws IOException {
    Resource resource = resourceLoader.getResource(mockIdpPath);

    if (!resource.exists()) {
      throw new IOException("File not found: " + mockIdpPath);
    }

    return new String(resource.getFile().toPath().toString());
  }
}
