import React, { useState, useEffect } from "react";
import { detailedDiff } from "deep-object-diff";
import { 
  List, 
  ListItem, 
  ListItemText, 
  ListItemIcon, 
  Chip, 
  Avatar, 
  Grid2 as Grid, 
  SxProps
} from "@mui/material";
import { Add, ChangeHistory, Remove, Face, Sync } from "@mui/icons-material";
import { amber, red, blue, green } from "@mui/material/colors";
import moment from "moment";
import { pond } from "protobuf-ts/pond";
import { useUserAPI } from "hooks";
import { useMobile } from "hooks";
import ObjectDescriber from "objects/ObjectDescriber";
import { makeStyles } from "@mui/styles";
import { alpha, Theme } from "@mui/material";
import ResponsiveTable, { Column } from "./ResponsiveTable";

const addedColour = alpha(green[600], 0.25);
const deletedColour = alpha(red[600], 0.25);
const updatedColour = alpha(blue[600], 0.25);
const resyncedColour = alpha(amber[600], 0.25);
const useStyles = makeStyles((_theme: Theme) => {
  return ({
    added: {
      backgroundColor: addedColour
    },
    deleted: {
      backgroundColor: deletedColour
    },
    updated: {
      backgroundColor: updatedColour
    },
    resynced: {
      backgroundColor: resyncedColour
    },
    chipContainer: {
      maxWidth: "100%",
      maxHeight: "100%",
      flexWrap: "wrap"
    },
    translateContainer: {
      borderLeft: "2px solid white",
      borderRight: "2px solid white",
      borderRadius: "6px",
      padding: "0 8px 0 8px",
      whiteSpace: "pre",
      margin: "8px"
    },
    translateBox: {
      display: "flex",
      flexDirection: "row",
      alignItems: "center",
      padding: "0px"
    }
  })
});

interface Diff {
  added: any;
  deleted: any;
  updated: any;
}

interface RowData {
  timestamp: string;
  user: string;
  changes: number;
  before: object;
  after: object;
  diff: Diff;
  status: string;
  type?: pond.ObjectType;
}

interface SummaryProps {
  changes: number;
  diff: Diff;
  before: any;
  after: any;
  kind: string;
  translateKey: (key: keyof any, type?: pond.ObjectType) => string;
  translateValue: (key: keyof any, obj: any, type?: pond.ObjectType) => string;
  variant?: "expanded" | "inline";
  type?: pond.ObjectType;
}

function Summary(props: SummaryProps) {
  const classes = useStyles();
  const { changes, diff, before, after, translateKey, translateValue, variant, type } = props;
  //console.log(diff)
  const { added, updated, deleted } = diff;

  const translate = (key: keyof any, changeType: "added" | "deleted" | "updated"): any => {
    switch (changeType) {
      case "updated":
        return (
          <div className={classes.translateBox}>
            <div>{translateKey(key, type) + ":"}</div>
            <div className={classes.translateContainer}>{translateValue(key, before, type)}</div>
            <div style={{ fontSize: "20px" }}>{"→"}</div>
            <div className={classes.translateContainer}>{translateValue(key, after, type)}</div>
          </div>
        );
      case "deleted":
        return translateKey(key, type);
      default:
        let val = translateValue(key, after, type);
        if (isBool(val)) {
          return translateKey(key, type);
        }
        return translateKey(key, type) + ": " + val;
    }
  };

  const isBool = (val: string) => {
    return val.toLowerCase() === "true" || val.toLowerCase() === "false";
  };

  const icon = (changeType: "added" | "deleted" | "updated") => {
    switch (changeType) {
      case "added":
        return <Add />;
      case "deleted":
        return <Remove />;
      default:
        return <ChangeHistory />;
    }
  };

  const inlineItem = (key: any, changeType: "added" | "deleted" | "updated") => {
    if (key !== "key") {
      return (
        <Grid key={key} style={{ minWidth: "8rem" }}>
          {key !== "key" && (
            <Chip
              className={
                changeType === "added"
                  ? classes.added
                  : changeType === "deleted"
                  ? classes.deleted
                  : classes.updated
              }
              icon={icon(changeType)}
              size="small"
              style={{ height: "100%", margin: "auto", padding: "0.25rem" }}
              label={<div style={{ whiteSpace: "pre" }}>{translate(key, changeType)}</div>}
            />
          )}
        </Grid>
      );
    }
    return null;
  };

  const isMobile = useMobile();

  const listItem = (key: any, changeType: "added" | "deleted" | "updated") => {
    return (
      <ListItem
        key={key}
        className={
          changeType === "added"
            ? classes.added
            : changeType === "deleted"
            ? classes.deleted
            : classes.updated
        }>
        <div style={{ marginRight: "1rem" }}>{icon(changeType)}</div>
        {isMobile ? (
          <div style={{ fontSize: "12px" }}>{translate(key, changeType)}</div>
        ) : (
          <ListItemText>{translate(key, changeType)}</ListItemText>
        )}
      </ListItem>
    );
  };

  switch (variant) {
    case "inline":
      if (changes <= 0) {
        return <Chip className={classes.resynced} icon={<Sync />} size="small" label="Resynced" />;
      }
      return (
        <Grid container direction="row" spacing={1} className={classes.chipContainer}>
          {added &&
            Object.keys(added).length > 0 &&
            Object.keys(added).map(key => inlineItem(key, "added"))}
          {deleted &&
            Object.keys(deleted).length > 0 &&
            Object.keys(deleted).map(key => inlineItem(key, "deleted"))}
          {updated &&
            Object.keys(updated).length > 0 &&
            Object.keys(updated).map(key => inlineItem(key, "updated"))}
        </Grid>
      );
    default:
      if (changes <= 0) {
        return (
          <List disablePadding>
            <ListItem key={"resynced"} className={classes.resynced}>
              <ListItemIcon>
                <Sync />
              </ListItemIcon>
              <ListItemText>
                Setting were resynced to ensure consistency with the cloud
              </ListItemText>
            </ListItem>
          </List>
        );
      }
      return (
        <List disablePadding>
          {added && Object.keys(added).length > 0 && (
            <React.Fragment>{Object.keys(added).map(key => listItem(key, "added"))}</React.Fragment>
          )}
          {deleted && Object.keys(deleted).length > 0 && (
            <React.Fragment>
              {Object.keys(deleted).map(key => listItem(key, "deleted"))}
            </React.Fragment>
          )}
          {updated && Object.keys(updated).length > 0 && (
            <React.Fragment>
              {Object.keys(updated).map(key => listItem(key, "updated"))}
            </React.Fragment>
          )}
        </List>
      );
  }
}

export interface Record {
  timestamp: string;
  data: any;
  user: string;
  status: string;
  type?: pond.ObjectType;
}

export interface ListResult {
  records: Record[];
  offset: number;
  total: number;
}

interface Props {
  name: string;
  kind: string;
  showTitle: boolean;
  list: (limit: number, offset: number) => Promise<ListResult>;
  translateKey: (key: keyof any, type?: pond.ObjectType) => string;
  translateValue: (key: keyof any, obj: any, type?: pond.ObjectType) => string;
  headerStyle?: React.CSSProperties;
  cellStyle?: SxProps<Theme>;
  noPaging?: boolean;
  sortingEnabled?: boolean;
  filteringEnabled?: boolean;
  drawer?: boolean;
}

export default function DiffHistory(props: Props) {
  const {
    kind,
    name,
    // headerStyle,
    cellStyle,
    list,
    translateKey,
    translateValue,
    showTitle,
    // noPaging,
    // sortingEnabled,
    // filteringEnabled,
    drawer
  } = props;
  const userAPI = useUserAPI();
  const [pageSize, setPageSize] = useState(10);
  const [profiles, setProfiles] = useState(new Map<string, pond.UserProfile>());
  const isMobile = useMobile();
  const [page, setPage] = useState(0);
  const [data, setData] = useState<RowData[]>([]);
  const [total, setTotal] = useState(0);

  useEffect(() => {
    new Promise(resolve => {
      list(pageSize, page * pageSize)
        .then((res: ListResult) => {
          let records = res.records;
          let uniqueUsers = Array.from(
            new Set(records.map((record: Record) => record.user).filter(id => !profiles.has(id)))
          );
          let req =
            uniqueUsers.length > 0
              ? userAPI.getProfiles(uniqueUsers)
              : new Promise<pond.UserProfile[]>(resolve => resolve([]));
          req
            .then((res: pond.UserProfile[]) => {
              res.forEach((profile: pond.UserProfile) => profiles.set(profile.id, profile));
              setProfiles(profiles);
            })
            .catch((err: any) => console.log(err))
            .finally(() => {
              let data: RowData[] = [];
              for (let i = 0; i < records.length; i++) {
                let record = records[i];
                let timestamp = record.timestamp;
                let user = record.user;
                //let typeString = record.type ? ObjectDescriber(record.type).name : kind;
                let before = {};
                if (i < records.length - 1) {
                  if (record.type) {
                    let found = false;
                    let k = i;
                    while (!found && k < records.length - 1) {
                      let nextRecord = records[k + 1];
                      if (record.type === nextRecord.type) {
                        if (record.type === pond.ObjectType.OBJECT_TYPE_DEVICE) {
                          if (record.data.deviceId === nextRecord.data.deviceId) {
                            before = nextRecord.data;
                            found = true;
                          }
                        } else if (
                          record.type === pond.ObjectType.OBJECT_TYPE_COMPONENT || 
                          record.type === pond.ObjectType.OBJECT_TYPE_INTERACTION
                        ) {
                          if (record.data.key === nextRecord.data.key) {
                            before = nextRecord.data;
                            found = true;
                          }
                        }
                      }
                      k++;
                    }
                  } else {
                    before = records[i + 1].data;
                  }
                }
                let after = record.data;
                let diff = detailedDiff(before, after) as Diff;
                let changes = countNumChanges(diff);
                let type = record.type;
                let status = record.status;

                data.push({
                  timestamp,
                  user,
                  changes,
                  before,
                  after,
                  diff,
                  status,
                  type
                });
              }
              setData(data);
              setTotal(res.total);
              resolve({ data: data, page: page, totalCount: res.total });
            });
        })
        .catch((_err: any) => {
          setData([]);
          resolve({ data: [], page: 0, totalCount: 0 });
        });
    });
  }, [pageSize, page, kind, list, profiles, userAPI]);

  const countNumChanges = (diff: Diff): number => {
    const { added, updated, deleted } = diff;
    let count = 0;
    count = count + Object.keys(added).length;
    count = count + Object.keys(deleted).length;
    count = count + Object.keys(updated).length;
    return count;
  };

  const getProfile = (id: string): pond.UserProfile => {
    if (id === "system") {
      return pond.UserProfile.create({ id: "system", name: "System" });
    }
    let profile = profiles.get(id);
    return profile ? profile : pond.UserProfile.create();
  };

  const columns = (): Column<RowData>[] => {
    return [{
      title: "Time",
      cellStyle: cellStyle,
      render: (row: RowData) => (
        <div style={isMobile || drawer ? { minWidth: "4rem" } : {}}>
          {moment(row.timestamp).calendar()}
        </div>
      ),
      // defaultSort: "asc",
      // cellStyle: cellStyle,
      // customSort: (a, b) => moment(b.timestamp).diff(moment(a.timestamp))
    },
    {
      title: "Object Type",
      cellStyle: cellStyle,
      render: row => {
        if (row.type) {
          return (<div>{ObjectDescriber(row.type).name}</div>)
        }
        return <div></div>
      }
    },
    {
      title: "User",
      cellStyle: cellStyle,
      // field: "user",
      render: row => {
        const profile = getProfile(row.user);
        const avatarURL = profile.avatar;
        const name = profile.name ? profile.name : "User " + profile.id;
        const avatar = avatarURL ? (
          <Avatar alt={name} src={avatarURL} />
        ) : (
          <Avatar alt={name}>
            <Face />
          </Avatar>
        );
        return (
          <div style={isMobile || drawer ? { minWidth: "4rem" } : {}}>
            <Chip variant="outlined" avatar={avatar} label={name} />
          </div>
        );
      },
      // customSort: (a, b) => a.user.localeCompare(b.user)
      //customFilterAndSearch: (filt: string, row: RowData) => row.user.toLowerCase().includes(filt.toLowerCase())
    },
    {
      title: "Status",
      cellStyle: cellStyle,
      render: row => <div>{row.status}</div>
    },
    {
      title: "Summary",
      cellStyle: cellStyle,
      hidden: isMobile || drawer,
      render: row => (
        <Summary
          variant="inline"
          kind={kind}
          changes={row.changes}
          before={row.before}
          after={row.after}
          diff={row.diff}
          translateKey={translateKey}
          translateValue={translateValue}
          type={row.type}
        />
      ),
      // sorting: false
    }
    ]
  }

  const handleRowsPerPageChange = (event: any) => {
    const newRowsPerPage = parseInt(event.target.value, 10); // Get selected value
    setPageSize(newRowsPerPage);
    setPage(0); // Reset to the first page
  }

  return (
    <ResponsiveTable<RowData>
      title={showTitle ? name + " History" : undefined}
      rows={data} 
      renderGutter={((row) => (
        <Summary
          variant="inline"
          kind={kind}
          changes={row.changes}
          before={row.before}
          after={row.after}
          diff={row.diff}
          translateKey={translateKey}
          translateValue={translateValue}
          type={row.type}
        />
      ))}
      columns={columns()}
      total={total} 
      pageSize={0} 
      page={0} 
      setPage={setPage} 
      handleRowsPerPageChange={handleRowsPerPageChange}
    />
  )

  // return (
  //   <MaterialTable
  //     title={name + " History"}
  //     onChangePage={e => {
  //       setPage(e);
  //     }}
  //     options={{
  //       paging: noPaging ? false : true,
  //       pageSize: pageSize,
  //       pageSizeOptions: [5, 10, 20],
  //       showTitle: showTitle,
  //       toolbar: showTitle,
  //       search: false,
  //       sorting: sortingEnabled ? true : false,
  //       columnsButton: showTitle,
  //       header: true,
  //       headerStyle: headerStyle,
  //       filtering: filteringEnabled
  //     }}
  //     localization={{
  //       body: { emptyDataSourceMessage: "No records found" }
  //     }}
  //     onChangeRowsPerPage={pageSize => setPageSize(pageSize)}
  //     columns={[
  //       {
  //         title: "Time",
  //         render: (row: RowData) => (
  //           <div style={isMobile || drawer ? { minWidth: "4rem" } : {}}>
  //             {moment(row.timestamp).calendar()}
  //           </div>
  //         ),
  //         defaultSort: "asc",
  //         cellStyle: cellStyle,
  //         customSort: (a, b) => moment(b.timestamp).diff(moment(a.timestamp))
  //       },
  //       {
  //         title: "Object Type",
  //         render: row => {
  //           if (row.type) {
  //             return ObjectDescriber(row.type).name;
  //           }
  //         }
  //       },
  //       {
  //         title: "User",
  //         cellStyle: cellStyle,
  //         field: "user",
  //         render: row => {
  //           const profile = getProfile(row.user);
  //           const avatarURL = profile.avatar;
  //           const name = profile.name ? profile.name : "User " + profile.id;
  //           const avatar = avatarURL ? (
  //             <Avatar alt={name} src={avatarURL} />
  //           ) : (
  //             <Avatar alt={name}>
  //               <Face />
  //             </Avatar>
  //           );
  //           return (
  //             <div style={isMobile || drawer ? { minWidth: "4rem" } : {}}>
  //               <Chip variant="outlined" avatar={avatar} label={name} />
  //             </div>
  //           );
  //         },
  //         customSort: (a, b) => a.user.localeCompare(b.user)
  //         //customFilterAndSearch: (filt: string, row: RowData) => row.user.toLowerCase().includes(filt.toLowerCase())
  //       },
  //       {
  //         title: "Status",
  //         render: row => row.status
  //       },
  //       {
  //         title: "Summary",
  //         cellStyle: cellStyle,
  //         hidden: isMobile || drawer,
  //         render: row => (
  //           <Summary
  //             variant="inline"
  //             kind={kind}
  //             changes={row.changes}
  //             before={row.before}
  //             after={row.after}
  //             diff={row.diff}
  //             translateKey={translateKey}
  //             translateValue={translateValue}
  //             type={row.type}
  //           />
  //         ),
  //         sorting: false
  //       }
  //     ]}
  //     icons={getTableIcons()}
  //     data={data}
  //     page={page}
  //     totalCount={total}
  //     detailPanel={row => {
  //       return (
  //         <Summary
  //           kind={kind}
  //           changes={row.changes}
  //           before={row.before}
  //           after={row.after}
  //           diff={row.diff}
  //           translateKey={translateKey}
  //           translateValue={translateValue}
  //           type={row.type}
  //         />
  //       );
  //     }}
  //     onRowClick={(event, row, togglePanel) => togglePanel && togglePanel()}
  //   />
  // );
}
