package invalid.lena.rsend

import android.content.Context
import android.util.AtomicFile
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.io.FileNotFoundException

// Config is rsend's whole state: the remote target, the schedule, and the
// folders to push. It is stored as plain JSON in app-private storage.

data class Remote(val host: String = "", val port: Int = 22, val user: String = "")

data class Schedule(
    val enabled: Boolean = false,
    val intervalMinutes: Int = 120,
    val wifiOnly: Boolean = true,
    val requireCharging: Boolean = false,
) {
    companion object {
        const val MIN_INTERVAL_MINUTES = 15
    }
}

data class Folder(
    val name: String = "",
    val local: String = "",
    val remote: String = "",
    val delete: Boolean = false,
    val excludes: List<String> = emptyList(),
)

data class Config(
    val remote: Remote = Remote(),
    val schedule: Schedule = Schedule(),
    val folders: List<Folder> = emptyList(),
) {
    fun toJson(): JSONObject = JSONObject().apply {
        put("remote", JSONObject().apply {
            put("host", remote.host)
            put("port", remote.port)
            put("user", remote.user)
        })
        put("schedule", JSONObject().apply {
            put("enabled", schedule.enabled)
            put("intervalMinutes", schedule.intervalMinutes)
            put("wifiOnly", schedule.wifiOnly)
            put("requireCharging", schedule.requireCharging)
        })
        put("folders", JSONArray().apply {
            folders.forEach { f ->
                put(JSONObject().apply {
                    put("name", f.name)
                    put("local", f.local)
                    put("remote", f.remote)
                    put("delete", f.delete)
                    put("excludes", JSONArray(f.excludes))
                })
            }
        })
    }

    companion object {
        fun file(ctx: Context): File = File(ctx.filesDir, "config.json")

        fun load(ctx: Context): Config {
            val f = file(ctx)
            return try {
                val text = AtomicFile(f).openRead().bufferedReader().use { it.readText() }
                fromJson(JSONObject(text))
            } catch (_: FileNotFoundException) {
                Config()
            } catch (e: Exception) {
                throw IllegalStateException("could not read ${f.name}: ${e.message}", e)
            }
        }

        // AtomicFile keeps the previous complete config if a write is interrupted.
        fun save(ctx: Context, c: Config) {
            val file = AtomicFile(file(ctx))
            val out = file.startWrite()
            try {
                out.write(c.toJson().toString(2).toByteArray())
                file.finishWrite(out)
            } catch (e: Exception) {
                file.failWrite(out)
                throw e
            }
        }

        fun fromJson(o: JSONObject): Config {
            val r = o.optJSONObject("remote") ?: JSONObject()
            val s = o.optJSONObject("schedule") ?: JSONObject()
            val fa = o.optJSONArray("folders") ?: JSONArray()
            val folders = ArrayList<Folder>(fa.length())
            for (i in 0 until fa.length()) {
                val fo = fa.getJSONObject(i)
                val ex = fo.optJSONArray("excludes") ?: JSONArray()
                val excludes = ArrayList<String>(ex.length())
                for (j in 0 until ex.length()) excludes.add(ex.getString(j))
                folders.add(
                    Folder(
                        name = fo.optString("name"),
                        local = fo.optString("local"),
                        remote = fo.optString("remote"),
                        delete = fo.optBoolean("delete", false),
                        excludes = excludes,
                    )
                )
            }
            return Config(
                remote = Remote(
                    host = r.optString("host"),
                    port = r.optInt("port", 22),
                    user = r.optString("user"),
                ),
                schedule = Schedule(
                    enabled = s.optBoolean("enabled", false),
                    intervalMinutes = s.optInt("intervalMinutes", 120)
                        .coerceAtLeast(Schedule.MIN_INTERVAL_MINUTES),
                    wifiOnly = s.optBoolean("wifiOnly", true),
                    requireCharging = s.optBoolean("requireCharging", false),
                ),
                folders = folders,
            )
        }
    }
}
