package com.secureline.server;

import android.content.Context;
import android.os.Environment;
import android.os.UserManager;
import android.util.Slog;

import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

public class SecureLineLicenseStore {

    private static final String TAG =
            "SecureLineLicenseStore";

    private static final String DIR =
            "secureline";

    private static final String FILE =
            "license.json";

    private long mExpiresDay = 0;

    private long mLicenseCounter = 0;

    private boolean mLoaded = false;

    private String mPlan = "";

    private final Context mContext;

    private File mLicenseFile;

    public SecureLineLicenseStore(Context context) {

        mContext = context;

        File dir =
                new File(
                        Environment.getDataSystemCeDirectory(0),
                        DIR
                );

        mLicenseFile =
                new File(dir, FILE);
    }

    // ============================================================
    // directory
    // ============================================================

    private File ensureDir() {

        File dir =
                new File(
                        Environment.getDataSystemCeDirectory(0),
                        DIR
                );

        if (!dir.exists()) {

            if (!dir.mkdirs()) {

                throw new IllegalStateException(
                        "SecureLine CE dir create failed"
                );
            }

            // owner only
            dir.setReadable(false, false);

            dir.setWritable(true, true);
        }

        return dir;
    }

    // ============================================================
    // disk load
    // ============================================================

    private void loadFromDisk() {

        try {

            if (!mLicenseFile.exists()) {

                Slog.i(
                        TAG,
                        "license file missing"
                );

                return;
            }

            byte[] bytes =
                    Files.readAllBytes(
                            mLicenseFile.toPath()
                    );

            JSONObject obj =
                    new JSONObject(
                            new String(
                                    bytes,
                                    StandardCharsets.UTF_8
                            )
                    );

            mExpiresDay =
                    obj.getLong("expires_day");

            mLicenseCounter =
                    obj.getLong("license_counter");

            mPlan =
                    obj.optString("plan", "");

            mLoaded = true;

            Slog.i(
                    TAG,
                    "license loaded"
                            + " expiresDay=" + mExpiresDay
                            + " counter=" + mLicenseCounter
                            + " plan=" + mPlan
            );

        } catch (Throwable t) {

            Slog.e(
                    TAG,
                    "license corrupt → reset",
                    t
            );

            deleteLicense();
        }
    }

    // ============================================================
    // disk save
    // ============================================================

    private void saveToDisk()
            throws Exception {

        File dir =
                ensureDir();

        mLicenseFile =
                new File(dir, FILE);

        JSONObject obj =
                new JSONObject();

        obj.put(
                "expires_day",
                mExpiresDay
        );

        obj.put(
                "license_counter",
                mLicenseCounter
        );

        obj.put(
                "plan",
                mPlan
        );

        byte[] data =
                obj.toString()
                        .getBytes(
                                StandardCharsets.UTF_8
                        );

        File tmp =
                new File(
                        mLicenseFile.getAbsolutePath()
                                + ".tmp"
                );

        try (FileOutputStream fos =
                     new FileOutputStream(tmp)) {

            fos.write(data);

            fos.flush();

            fos.getFD().sync();
        }

        if (!tmp.renameTo(mLicenseFile)) {

            throw new IllegalStateException(
                    "license rename failed"
            );
        }

        Slog.i(
                TAG,
                "license persisted"
                        + " expiresDay=" + mExpiresDay
                        + " counter=" + mLicenseCounter
        );
    }

    // ============================================================
    // load from identity (called after AFU)
    // ============================================================

    public void loadFromIdentity(
            byte[] identity
    ) {

        if (identity == null) {

            Slog.d(
                    TAG,
                    "identity null → skip license load"
            );

            return;
        }

        if (!UserManager.get(mContext)
                .isUserUnlocked()) {

            Slog.d(
                    TAG,
                    "CE locked → skip license load"
            );

            return;
        }

        loadFromDisk();
    }

    // ============================================================
    // update license (called after activation)
    // ============================================================

    public void updateLicense(
            JSONObject license
    ) throws Exception {

        long expires =
                license.getLong("expires_day");

        long counter =
                license.getLong("license_counter");

        if (counter <= mLicenseCounter) {

            Slog.w(
                    TAG,
                    "license rollback blocked"
                            + " old=" + mLicenseCounter
                            + " new=" + counter
            );

            return;
        }

        mExpiresDay =
                expires;

        mLicenseCounter =
                counter;

        mPlan =
                license.optString(
                        "plan",
                        mPlan
                );

        mLoaded = true;

        saveToDisk();

        Slog.i(
                TAG,
                "license updated"
                        + " expiresDay=" + expires
                        + " counter=" + counter
                        + " plan=" + mPlan
        );
    }

    // ============================================================
    // license state
    // ============================================================

    public int getLicenseDaysLeft() {

        if (!mLoaded) {

            return 0;
        }

        long today =
                System.currentTimeMillis()
                        / 86400000L;

        return (int) Math.max(
                mExpiresDay - today,
                0
        );
    }

    public boolean isLicenseActive() {

        return getLicenseDaysLeft() > 0;
    }

    public boolean licenseExists() {

        return mLoaded;
    }

    public long getLicenseCounter() {

        return mLicenseCounter;
    }

    public String getLicensePlan() {

        return mPlan;
    }

    // ============================================================
    // required by policy + manager
    // ============================================================

    public long getExpiresDay() {

        return mExpiresDay;
    }

    // ============================================================
    // delete
    // ============================================================

    public void deleteLicense() {

        try {

            if (mLicenseFile.exists()) {

                mLicenseFile.delete();
            }

        } catch (Throwable ignored) {}

        mLoaded = false;

        mExpiresDay = 0;

        mLicenseCounter = 0;

        mPlan = "";

        Slog.w(
                TAG,
                "license deleted"
        );
    }
}
