<%@ Page Language="C#" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Linq" %>
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Web.Hosting" %>
<%@ Import Namespace="System.Web.Script.Serialization" %>
<%@ Import Namespace="mojoPortal.Business" %>
<%@ Import Namespace="mojoPortal.Business.WebHelpers" %>
<%@ Import Namespace="mojoPortal.Web.Admin" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Clear();
        Response.Expires = -1;
        Response.ContentType = "application/json";

        try
        {
            Dictionary<string, object> jsonData = ReadJsonBody();
            string action = GetAction(jsonData);

            if (!IsAuthorized())
            {
                Response.StatusCode = 403;
                WriteJson(new { success = false, error = "Access denied" });
                return;
            }

            if (action == "siphon" || action == "migrate")
            {
                if (!string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    Response.StatusCode = 405;
                    WriteJson(new { success = false, error = "Method not allowed" });
                    return;
                }

                HandleSiphon(jsonData);
                return;
            }

            new MigrationAnalyzer().ProcessRequest(Context);
        }
        catch (Exception ex)
        {
            WriteJson(new { success = false, error = ex.Message, detail = ex.ToString() });
        }
        finally
        {
            Context.ApplicationInstance.CompleteRequest();
        }
    }

    private bool IsAuthorized()
    {
        if (!Request.IsAuthenticated || !WebUser.IsAdmin)
        {
            return false;
        }

        SiteSettings siteSettings = CacheHelper.GetCurrentSiteSettings();
        return siteSettings != null && siteSettings.IsServerAdminSite;
    }

    private Dictionary<string, object> ReadJsonBody()
    {
        if (Request.InputStream == null) return null;

        Request.InputStream.Position = 0;
        string requestBody = new StreamReader(Request.InputStream).ReadToEnd();
        if (string.IsNullOrEmpty(requestBody)) return null;

        try
        {
            return new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(requestBody);
        }
        catch
        {
            return null;
        }
    }

    private string GetAction(Dictionary<string, object> jsonData)
    {
        string action = jsonData != null && jsonData.ContainsKey("action")
            ? Convert.ToString(jsonData["action"])
            : Request.Form["action"] ?? Request.QueryString["action"] ?? "status";

        return (action ?? string.Empty).ToLowerInvariant();
    }

    private void HandleSiphon(Dictionary<string, object> jsonData)
    {
        string currentDbPath = HostingEnvironment.MapPath("~/Data/sqlitedb/mojo.db.config");
        string sourceDbPath = ResolveRequestedSource(jsonData);
        int siteId = jsonData != null && jsonData.ContainsKey("siteId") ? Convert.ToInt32(jsonData["siteId"]) : 1;

        if (!File.Exists(sourceDbPath) || !File.Exists(currentDbPath))
        {
            WriteJson(new { success = false, error = "Database files not found" });
            return;
        }

        object engine = CreateMigrationEngine(sourceDbPath, currentDbPath);
        if (engine == null)
        {
            WriteJson(new { success = false, error = "Could not load LegacyDataMigrationEngine from deployed data assembly" });
            return;
        }

        var pageIdMap = new Dictionary<int, int>();
        int pagesCount = InvokeEngineInt(engine, "MigratePages", siteId, pageIdMap);
        int modulesCount = InvokeEngineInt(engine, "MigrateModules", siteId, pageIdMap);
        int settingsCount = InvokeEngineInt(engine, "MigrateSiteSettings", siteId);
        var logs = GetEngineLogs(engine);

        WriteJson(new
        {
            success = true,
            state = "Filled",
            sourceFile = Path.GetFileName(sourceDbPath),
            pages = pagesCount,
            modules = modulesCount,
            siteSettings = settingsCount,
            logs = logs
        });
    }

    private string ResolveRequestedSource(Dictionary<string, object> jsonData)
    {
        string dbFolder = HostingEnvironment.MapPath("~/Data/sqlitedb");

        if (jsonData != null && jsonData.ContainsKey("sourceFile"))
        {
            return Path.Combine(dbFolder, Path.GetFileName(Convert.ToString(jsonData["sourceFile"]) ?? string.Empty));
        }

        if (jsonData != null && jsonData.ContainsKey("sourcePath"))
        {
            string sourcePath = Convert.ToString(jsonData["sourcePath"]);
            if (!string.IsNullOrEmpty(sourcePath) && Path.IsPathRooted(sourcePath)) return sourcePath;
        }

        return Path.Combine(dbFolder, "legacy.db.config");
    }

    private object CreateMigrationEngine(string sourceDbPath, string currentDbPath)
    {
        string[] typeNames =
        {
            "mojoPortal.Data.Migration.LegacyDataMigrationEngine, mojoPortal.Data",
            "mojoPortal.Data.Migration.LegacyDataMigrationEngine, mojoPortal.Data.SQLite"
        };

        foreach (string typeName in typeNames)
        {
            Type engineType = Type.GetType(typeName, false);
            if (engineType != null)
            {
                return Activator.CreateInstance(engineType, sourceDbPath, currentDbPath);
            }
        }

        return null;
    }

    private int InvokeEngineInt(object engine, string methodName, params object[] args)
    {
        var methods = engine.GetType().GetMethods()
            .Where(m => string.Equals(m.Name, methodName, StringComparison.Ordinal))
            .ToArray();

        var method = methods.FirstOrDefault(m => ParametersMatch(m.GetParameters(), args));
        if (method == null) return 0;

        object value = method.Invoke(engine, args);
        return value != null ? Convert.ToInt32(value) : 0;
    }

    private bool ParametersMatch(ParameterInfo[] parameters, object[] args)
    {
        if (parameters.Length != args.Length) return false;

        for (int i = 0; i < parameters.Length; i++)
        {
            object arg = args[i];
            Type parameterType = parameters[i].ParameterType;

            if (arg == null)
            {
                if (parameterType.IsValueType && Nullable.GetUnderlyingType(parameterType) == null) return false;
                continue;
            }

            if (!parameterType.IsAssignableFrom(arg.GetType())) return false;
        }

        return true;
    }

    private IEnumerable<string> GetEngineLogs(object engine)
    {
        var property = engine.GetType().GetProperty("MigrationLog");
        if (property == null) return Enumerable.Empty<string>();
        return property.GetValue(engine, null) as IEnumerable<string> ?? Enumerable.Empty<string>();
    }

    private void WriteJson(object data)
    {
        Response.Write(new JavaScriptSerializer().Serialize(data));
    }
</script>
