package ru.kirillius.pf.sdn.core.Auth;

import org.json.JSONArray;
import org.json.JSONTokener;
import ru.kirillius.json.JSONUtility;
import ru.kirillius.pf.sdn.core.AppService;
import ru.kirillius.pf.sdn.core.Context;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;


/**
 * Persists and manages authentication tokens on disk for API consumers.
 */
public class TokenService extends AppService {
    private final File file;

    /**
     * Loads existing tokens from disk and prepares the storage file.
     */
    public TokenService(Context context) {
        super(context);
        file = new File(context.getConfig().getCacheDirectory(), "tokens.json");
        if (file.exists()) {
            try (var stream = new FileInputStream(file)) {
                var json = new JSONArray(new JSONTokener(stream));
                tokens = JSONUtility.deserializeCollection(json, AuthToken.class, null).collect(Collectors.toList());
            } catch (IOException e) {
                throw new RuntimeException("Failed to read token storage", e);
            }
        } else {
            tokens = new ArrayList<>();
        }
    }

    private final List<AuthToken> tokens;

    /**
     * Returns an immutable view over the stored tokens.
     */
    public Collection<AuthToken> getTokens() {
        synchronized (tokens) {
            return Collections.unmodifiableList(tokens);
        }
    }

    /**
     * Adds the provided tokens to the storage.
     */
    public void add(AuthToken... what) {
        synchronized (tokens) {
            Collections.addAll(tokens, what);
        }
    }

    /**
     * Checks whether the specified token is present in storage.
     */
    public boolean contains(AuthToken what) {
        synchronized (tokens) {
            return tokens.contains(what);
        }
    }

    /**
     * Removes the provided tokens from storage.
     */
    public void remove(AuthToken... what) {
        synchronized (tokens) {
            Arrays.stream(what).forEach(tokens::remove);
        }
    }

    /**
     * Writes the in-memory token list to disk.
     */
    public synchronized void store() throws IOException {
        try (var fileInputStream = new FileOutputStream(file)) {
            try (var writer = new BufferedWriter(new OutputStreamWriter(fileInputStream))) {
                writer.write(JSONUtility.serializeCollection(tokens, AuthToken.class, null).toString());
                writer.flush();
            }
        }
    }

    /**
     * No-op close implementation.
     */
    @Override
    public void close() throws IOException {
        //no-op
    }
}