// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import m from 'mithril';
import {Icons} from '../../base/semantic_icons';
import {assertTrue} from '../../base/assert';
import {z} from 'zod';
import {Trace} from '../../public/trace';
import {Button} from '../../widgets/button';
import {Stack} from '../../widgets/stack';

const QUERY_HISTORY_KEY = 'queryHistory';

export interface QueryHistoryComponentAttrs {
  readonly className?: string;
  trace: Trace;
  runQuery: (query: string) => void;
  setQuery: (query: string) => void;
}

export class QueryHistoryComponent
  implements m.ClassComponent<QueryHistoryComponentAttrs>
{
  view({attrs}: m.CVnode<QueryHistoryComponentAttrs>) {
    const {trace, runQuery, setQuery, ...rest} = attrs;
    const unstarred: HistoryItemComponentAttrs[] = [];
    const starred: HistoryItemComponentAttrs[] = [];
    // Items are stored with most recent first (index 0)
    // Iterate forward and separate starred from unstarred
    for (let i = 0; i < queryHistoryStorage.data.length; i++) {
      const entry = queryHistoryStorage.data[i];
      const arr = entry.starred ? starred : unstarred;
      arr.push({trace, index: i, entry, runQuery, setQuery});
    }
    return m(
      '.pf-query-history',
      {
        ...rest,
      },
      m(
        '.pf-query-history__header',
        `Query history (${queryHistoryStorage.data.length} queries)`,
      ),
      starred.map((attrs) => m(HistoryItemComponent, attrs)),
      unstarred.map((attrs) => m(HistoryItemComponent, attrs)),
    );
  }
}

export interface HistoryItemComponentAttrs {
  trace: Trace;
  index: number;
  entry: QueryHistoryEntry;
  runQuery: (query: string) => void;
  setQuery: (query: string) => void;
}

export class HistoryItemComponent
  implements m.ClassComponent<HistoryItemComponentAttrs>
{
  view(vnode: m.Vnode<HistoryItemComponentAttrs>): m.Child {
    const query = vnode.attrs.entry.query;
    return m(
      '.pf-query-history__item',
      m(
        Stack,
        {
          className: 'pf-query-history__item-buttons',
          orientation: 'horizontal',
        },
        [
          m(Button, {
            onclick: () => {
              queryHistoryStorage.setStarred(
                vnode.attrs.index,
                !vnode.attrs.entry.starred,
              );
            },
            icon: Icons.Star,
            iconFilled: vnode.attrs.entry.starred,
          }),
          m(Button, {
            onclick: () => vnode.attrs.setQuery(query),
            icon: Icons.Edit,
          }),
          m(Button, {
            onclick: () => vnode.attrs.runQuery(query),
            icon: Icons.Play,
          }),
          m(Button, {
            onclick: () => {
              queryHistoryStorage.remove(vnode.attrs.index);
            },
            icon: Icons.Delete,
          }),
        ],
      ),
      m(
        'pre',
        {
          onclick: () => vnode.attrs.setQuery(query),
          ondblclick: () => vnode.attrs.runQuery(query),
        },
        query,
      ),
    );
  }
}

class HistoryStorage {
  data: QueryHistory;
  maxItems = 50;

  constructor() {
    this.data = this.load();
  }

  saveQuery(query: string) {
    const items = this.data;
    let lastUnstarred = -1;
    let countUnstarred = 0;
    let existingIndex = -1;

    for (let i = 0; i < items.length; i++) {
      if (!items[i].starred) {
        countUnstarred++;
        lastUnstarred = i;
      }

      if (items[i].query === query) {
        existingIndex = i;
      }
    }

    // If query already exists, move it to the front (index 0)
    if (existingIndex !== -1) {
      const isStarred = items[existingIndex].starred;
      items.splice(existingIndex, 1);
      // Re-add at the front with same starred status
      items.unshift({query, starred: isStarred});
      this.save();
      return;
    }

    // Check if we need to remove the oldest unstarred query
    if (countUnstarred >= this.maxItems) {
      assertTrue(lastUnstarred !== -1);
      items.splice(lastUnstarred, 1);
    }

    // Add new query at the front as unstarred
    items.unshift({query, starred: false});
    this.save();
  }

  setStarred(index: number, starred: boolean) {
    assertTrue(index >= 0 && index < this.data.length);
    this.data[index].starred = starred;
    this.save();
  }

  remove(index: number) {
    assertTrue(index >= 0 && index < this.data.length);
    this.data.splice(index, 1);
    this.save();
  }

  private load(): QueryHistory {
    const value = window.localStorage.getItem(QUERY_HISTORY_KEY);
    if (value === null) {
      return [];
    }
    const res = QUERY_HISTORY_SCHEMA.safeParse(JSON.parse(value));
    return res.success ? res.data : [];
  }

  private save() {
    window.localStorage.setItem(QUERY_HISTORY_KEY, JSON.stringify(this.data));
  }
}

const QUERY_HISTORY_ENTRY_SCHEMA = z.object({
  query: z.string(),
  starred: z.boolean().default(false),
});

type QueryHistoryEntry = z.infer<typeof QUERY_HISTORY_ENTRY_SCHEMA>;

const QUERY_HISTORY_SCHEMA = z.array(QUERY_HISTORY_ENTRY_SCHEMA);

type QueryHistory = z.infer<typeof QUERY_HISTORY_SCHEMA>;

export const queryHistoryStorage = new HistoryStorage();
