<template>
  <v-container fluid>

    <v-overlay :value="loading && !users" absolute>
      <v-progress-circular
        indeterminate
        size="64"
      ></v-progress-circular>
    </v-overlay>

    <v-overlay :value="!users && !loading" absolute opacity="0.8">
      <v-row justify="center">
        <v-alert
          type="error"
        >
          The configuration could not be loaded.
        </v-alert>
      </v-row>
      <v-row justify="center">
        <v-btn color="primary" @click="getUsers">Retry</v-btn>
      </v-row>
    </v-overlay>

    <v-row>
      <v-col cols="4" max-height="100%">
        <v-toolbar
          dense
          flat
        >
          <v-toolbar-title>
            User management
          </v-toolbar-title>
          <v-btn v-if="!showIP && !showHost"
            class="ml-3"
            small
            icon
            title="Show password field (allows changing a user's password)"
            @click="showPassword = !showPassword"
          >
            <v-icon small>{{ showPassword ? "mdi-eye" : "mdi-eye-off" }}</v-icon>
          </v-btn>
          <v-spacer/>
          <template v-if="isDirty">
            <v-icon color="warning" @click="reset" title="Discard changes">mdi-undo</v-icon>
            <v-spacer/>
            <v-icon left color="primary" @click="saveToFile" title="Save changes to file">mdi-content-save-outline</v-icon>
            <v-icon color="primary" @click="upload" title="Upload changes to server">mdi-cloud-upload</v-icon>
          </template>
        </v-toolbar>

        <!--
        <v-btn
          class="mb-5"
          color="primary"
          small
          @click="addUser"
        >
          <v-icon left small>mdi-account-plus-outline</v-icon>
          Add user
        </v-btn>
        -->

        <v-menu
          offset-y
        >
          <template v-slot:activator="{ on, attrs }">
            <v-btn
              class="mb-5"
              color="primary"
              small
              v-bind="attrs"
              v-on="on"
            >
              <v-icon left small>mdi-account-plus-outline</v-icon>
              Add user
            </v-btn>
          </template>

          <v-list>
            <v-list-item>
              <v-btn
                class="mb-5"
                width="100%"
                color="blue"
                dark
                small
                @click="addUser({password: true})"
              >
                Add named user
              </v-btn>
            </v-list-item>
            <v-list-item>
              <v-btn
                class="mb-5"
                width="100%"
                color="green"
                dark
                small
                @click="addUser({ip: true})"
              >
                Add IP user
              </v-btn>
            </v-list-item>
            <v-list-item>
              <v-btn
                class="mb-5"
                width="100%"
                color="cyan"
                dark
                small
                @click="addUser({host: true})"
              >
                Add host user
              </v-btn>
            </v-list-item>
          </v-list>

        </v-menu>

        <v-treeview
          ref="tree"
          dense
          open-on-click
          activatable
          hoverable
          :multiple-active="false"
          :active.sync="active"
          :open.sync="open"
          :items="treeview"
          style="cursor:pointer;"
        >
          <template v-slot:prepend="{ item, open }">
            <v-icon v-if="userIsDirty(item.value)"
              small
              color="warning"
              title="Discard changes"
              @click="reset(item)"
            >mdi-undo</v-icon>
          </template>
          <template v-slot:label="{ item, open }">
            <v-chip v-if="item.value"
              small
            >
              {{ item.id }}
            </v-chip>
            <template v-else>
              {{ item.id }}
            </template>
          </template>
          <template v-slot:append="{ item, open }">
            <v-icon v-if="userIsDirty(item.value)"
              small
              color="primary"
              title="Save changes"
              @click.stop="saveUser(item.value)"
            >mdi-cloud-upload</v-icon>
            <v-btn v-else-if="item.value"
              small
              icon
              @click="remove(item)"
            >
              <v-icon
                small
                color="danger"
                title="Delete this user"
              >mdi-trash-can-outline</v-icon>
            </v-btn>
          </template>
        </v-treeview>

      </v-col>

      <v-col cols="8">
        <dougal-user-settings v-if="newUser"
          :self="user"
          :show-ip="showIP"
          :show-host="showHost"
          :show-password="showPassword"
          v-model="newUser"
        >
          <template v-slot:actions="{ hasErrors }">
            <v-btn
              color="primary"
              :disabled="hasErrors"
              @click="saveUser(newUser)"
            >Save</v-btn>
            <v-spacer></v-spacer>
            <v-btn
              color="warning"
              @click="newUser=null"
            >Cancel</v-btn>
          </template>
        </dougal-user-settings>
        <dougal-user-settings v-else-if="activeUser"
          :self="user"
          :show-password="showPassword"
          v-model="activeUser"
        >
          <template v-slot:actions="{ hasErrors, dirty }">
            <v-btn
              v-if="dirty"
              color="primary"
              :disabled="hasErrors"
              @click="saveUser(activeUser)"
            >Save</v-btn>
            <v-spacer></v-spacer>
            <v-btn
              v-if="dirty"
              color="warning"
              @click="reset(activeUser)"
            >Cancel</v-btn>
          </template>
        </dougal-user-settings>
        <v-card v-else>
          <v-card-text>
            Select an user to edit from the list
          </v-card-text>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>


<script>
import YAML from 'yaml';
import { mapActions, mapGetters } from 'vuex';
import { deepSet } from '@/lib/utils';
import { User } from '@/lib/user';

import DougalUserSettings from '@/components/user-settings';

export default {
  name: "DougalUsers",

  components: {
    DougalUserSettings
  },

  data () {
    return {
      users: [],
      active: [],
      open: [],
      newUser: null,
      showIP: null,
      showHost: null,
      showPassword: null,
    };
  },

  computed: {

    isDirty () {
      return this.users.some( user => user.dirty );
    },

    treeview () {
      const tree = {};

      for (const user of this.users) {
        if (!tree[user.name]) {
          tree[user.name] = {
            id: user.name,
            name: user.name,
            children: []
          }
        }

        tree[user.name].children.push({
          id: user.id,
          name: user.id,
          value: user
        });
      }

      return Object.values(tree);
    },

    activeID () {
      return this.active[0];
    },

    activeUser: {
      get () {
        if (this.activeID) {
          return this.users.find(i => i.id == this.activeID);
        }
      },

      set (v) {
        if (this.activeID) {
          const idx = this.users.findIndex( i => i.id == this.activeID );
          if (idx != -1) {
            this.users.splice(idx, 1, v);
          }
        }
      }
    },

    ...mapGetters(['user', 'loading', 'serverEvent'])
  },

  methods: {

    async getUsers () {
      return await User.fromAPI(this.api) ?? [];
      /*
      const url = `/user`;
      const init = {
        headers: {
          "If-None-Match": "" // Ensure we get a fresh response
        }
      };
      return await this.api([url, init]);
      */
    },

    userIsDirty (user) {
      return user?.dirty;
    },

    addUser (opts={}) {

      this.showIP = opts?.ip;
      this.showHost = opts?.host;
      this.showPassword = opts?.password;

      this.newUser = this.user.spawn();
      // Do not add the wildcard org by default.
      // It can still be added by hand if needed
      this.newUser.organisations.remove("*");
    },

    async saveUser (user) {
      if (user.api) {
        console.log("Existing user");
      } else {
        console.log("New user");
      }
      if (user == this.newUser) {
        console.log("POST new user");
      } else {
        console.log("PUT existing user");
      }
      console.log(user);
      return await user.save(this.api);
    },

    async saveAll () {
      let count = 0;
      const dirtyUsers = this.users.filter( user => user.dirty );
      for (const user of dirtyUsers) {
        this.showSnack([`Saving ${user.name} – ${++count} of ${dirtyUsers.length} users`, "info"]);
        await user.save(this.api);
      }
      if (count) {
        this.showSnack([`${dirtyUsers.length} users saved`, "success"]);
      }
    },

    async remove (item) {
      // TODO: Ask for confirmation before removing!
      const url = `/user/${item.id}`;
      const init = { method: "DELETE" };
      const res = await this.api([url, init]);
      this.reset();
    },

    replace(user) {
      const idx = this.users.findIndex(i => i.id == user.id);
      if (idx != -1) {
        this.users.splice(idx, 1, user);
      }
    },

    async saveToFile () {
      const payload = YAML.stringify(this.users);
      const blob = new Blob([payload], {type: "application/yaml"});
      const url = URL.createObjectURL(blob);
      const filename = "dougal-users.yaml";

      const element = document.createElement('a');
      element.download = filename;
      element.href = url;
      element.click();
      URL.revokeObjectURL(url);
    },

    async upload () {
      let success = true;
      const dirty = this.users.filter(i => i.$isDirty);
      let count = 0;
      for (const user of dirty) {
        const body = {...user};
        delete body.$isDirty;
        const url = `/user/${user.id}`;
        const init = {
          method: "PUT",
          headers: {
            "Content-Type": "application/json"
          },
          body
        };
        const res = await this.api([url, init]);
        if (res && res.id) {
          // In case the server decided to apply any changes
          this.showSnack([`User ${user.id} uploaded to server (${++count}/${dirty.length})`, "success"]);
          this.$nextTick( () => {
            this.replace(res);
          });
        } else {
          success = false;
          this.showSnack([`Failed to save user ${user.name} (${user.id})`, "warning"])
        }
      }
    },

    async reset (item) {
      this.active = [];
      this.open = [];
      const users = await this.getUsers();

      if (item) {
        // Reset only this user
        const id = item.id;

        const idx0 = this.users.findIndex(i => i.id == id);
        const idx1 = users.findIndex(i => i.id == id);

        if (idx0 != -1 && idx1 != -1) {
          this.users.splice(idx0, 1, users[idx1]);
        }
      } else {
        // Reset all
        this.users = users;
      }
      this.$refs.tree.updateAll(true);
    },

    ...mapActions(["api", "showSnack"])

  },

  async mounted () {
    await this.reset();
    this.$refs.tree.updateAll(true);
  },

}
</script>
