import 'dart:io';

import 'package:intl/intl.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart';
import 'package:toml/toml.dart';

class DartboardData {
  DartboardData({
    required this.fullName,
    required this.phoneNumber,
    required this.email,
    // required this.address,
    required this.imagePath,
    required this.dartboardTheme,
    required this.experiences,
    required this.miscList,
  });

  factory DartboardData.fromToml(String tomlFilePath) {
    final tomlData = TomlDocument.loadSync(tomlFilePath).toMap();

    final List<DartboardExperience> exps = tomlData.entries
        .where((e) => e.value is List && (e.value as List).firstOrNull is Map)
        .map((MapEntry<String, dynamic> expsEntry) {
          final String subsection = tomlData['${expsEntry.key}_name'] as String? ?? '';
          final exps = (expsEntry.value as List).map((e) => e as Map<String, dynamic>).toList();
          return exps.map((exp) {
            final TomlLocalDate? start = exp['start'] as TomlLocalDate?;
            final TomlLocalDate? end = exp['end'] as TomlLocalDate?;
            return DartboardExperience(
              title: exp['title'] as String,
              subsection: subsection,
              range: DateRange(
                start: start == null ? null : DateTime(start.date.year, start.date.month),
                end: end == null ? null : DateTime(end.date.year, end.date.month),
              ),
              attributes: (exp['attributes'] as List).map((e) => DartboardText(content: e as String)).toList(),
              location: exp['location'] as String?,
            );
          }).toList();
        })
        .expand((i) => i)
        .toList();
    return DartboardData(
      fullName: tomlData['full_name'] as String,
      phoneNumber: tomlData['phone_number'] as String,
      email: tomlData['email'] as String,
      imagePath: tomlData['image'] as String,
      dartboardTheme: DartboardTheme.fromToml(tomlData),
      experiences: exps,
      miscList: [
        DartboardList(subsection: 'Skills & Interest', content: 'Proficient in flutter and other'),
      ],
    );
  }

  final String fullName;
  final String phoneNumber;
  final String email;
  // final String address;
  final String imagePath;
  final DartboardTheme dartboardTheme;
  final List<DartboardExperience> experiences;
  final List<DartboardList> miscList;

  Font get font => dartboardTheme.font;

  PdfColor get primaryColor => dartboardTheme.primaryColor;
  PdfColor get accentColor => dartboardTheme.accentColor;
  PdfColor get backgroundColor => dartboardTheme.backgroundColor;

  TextStyle get headerTextStyle =>
      TextStyle(fontSize: 18, font: font, fontWeight: FontWeight.bold, color: const PdfColorGrey(0.18));
  TextStyle get subheaderTextStyle => TextStyle(fontSize: 14, font: font, color: const PdfColorGrey(0.3));
  TextStyle get defaultTextStyle => TextStyle(fontSize: 10, font: font, color: const PdfColorGrey(0.45));

  Map<String, List<DartboardExperience>> get groupedExperiences {
    final Map<String, List<DartboardExperience>> exps = {};
    for (final DartboardExperience exp in experiences) {
      if (!exps.containsKey(exp.subsection)) {
        exps[exp.subsection] = <DartboardExperience>[];
      }
      exps[exp.subsection]!.add(exp);
    }
    return exps;
  }

  @override
  int get hashCode {
    return Object.hashAll([fullName, phoneNumber, email, imagePath, ...experiences]);
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

class DartboardExperience {
  DartboardExperience({
    required this.title,
    required this.attributes,
    required this.range,
    required this.subsection,
    this.location,
  });
  final String subsection;
  final String title;
  String? location;
  final List<DartboardText> attributes;
  final DateRange range;

  @override
  int get hashCode {
    return Object.hashAll([subsection, title, ...attributes, range]);
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

class DateRange {
  DateRange({required this.start, required this.end});
  final DateTime? start;
  final DateTime? end;

  @override
  String toString() {
    final DateFormat dateFormat = DateFormat("MMM yyyy");
    if (start == null && end == null) {
      return '';
    }
    if (start == null && end != null) {
      return dateFormat.format(end!);
    }
    if (start != null && end == null) {
      return "${dateFormat.format(start!)} - Current";
    }
    return "${dateFormat.format(start!)} - ${dateFormat.format(end!)}";
  }

  @override
  int get hashCode {
    return Object.hashAll([start, end]);
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

class DartboardList {
  DartboardList({required this.subsection, required this.content});
  final String subsection;
  final String content;
  @override
  int get hashCode {
    return Object.hashAll([subsection, content]);
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

class DartboardTheme {
  DartboardTheme({
    required this.primaryHex,
    required this.accentHex,
    required this.backgroundHex,
    required this.fontPath,
    required this.bulletPoint,
  });

  factory DartboardTheme.fromToml(Map<String, dynamic> toml) => DartboardTheme(
        primaryHex: (toml['theme'] as Map)['primary_hex'] as String,
        accentHex: (toml['theme'] as Map)['accent_hex'] as String,
        backgroundHex: (toml['theme'] as Map)['background_hex'] as String,
        fontPath: (toml['theme'] as Map)['font'] as String,
        bulletPoint: (toml['theme'] as Map)['bullet_point'] as String? ?? '-',
      );

  factory DartboardTheme.retro() => DartboardTheme(
        primaryHex: "00AA00",
        accentHex: "44EE66",
        backgroundHex: "FFFFFF",
        fontPath: "nerd.ttf",
        bulletPoint: '-',
      );

  static const double inch = 72.0;
  static const double cm = inch / 2.54;
  static const double mm = inch / 25.4;

  static const width = 21.0 * cm;
  static const height = 29.7 * cm;
  static const margin = 2.0 * cm;

  final String primaryHex;
  final String accentHex;
  final String backgroundHex;
  final String fontPath;
  final String bulletPoint;
  Font? _font;

  PdfColor get primaryColor => PdfColor.fromHex(primaryHex);
  PdfColor get accentColor => PdfColor.fromHex(accentHex);
  PdfColor get backgroundColor => PdfColor.fromHex(backgroundHex);

  Font get font {
    _font ??= Font.ttf(File(fontPath).readAsBytesSync().buffer.asByteData());
    return _font!;
  }

  @override
  int get hashCode {
    return Object.hashAll([primaryHex, accentHex, backgroundHex, fontPath, bulletPoint]);
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

enum DartboardTextType { normal, linkText }

typedef DartboardTextLinkData = ({String text, String? url, DartboardTextType type});

/// Automatically detects and parses strings with hypertext in a markdown format
class DartboardText {
  DartboardText({required this.content});

  final String content;
  static final _markdownLinkRegex = RegExp(r'\[(.*?)\]\((.*?)\)');

  bool get hasLinkMarkup => _markdownLinkRegex.hasMatch(content);

  List<DartboardTextLinkData> toTextLinkList() {
    final markdownLinkRegex = RegExp(r'\[(.*?)\]\((.*?)\)');
    if (markdownLinkRegex.hasMatch(content)) {
      final matches = markdownLinkRegex.allMatches(content).toList();
      final List<DartboardTextLinkData> stringSections = [];
      int prevStartIndex = 0;
      while (matches.isNotEmpty) {
        final match = matches.removeAt(0);
        stringSections
            .add((text: content.substring(prevStartIndex, match.start), url: null, type: DartboardTextType.normal));
        stringSections.add((text: match.group(1)!, url: match.group(2), type: DartboardTextType.linkText));
        prevStartIndex = match.end;
      }
      stringSections
          .add((text: content.substring(prevStartIndex, content.length), url: null, type: DartboardTextType.normal));
      return stringSections;
    } else {
      return [(text: content, url: null, type: DartboardTextType.normal)];
      // return Text("${bulletString != null ? '$bulletString ' : ''}$content", style: style);
    }
  }

  @override
  int get hashCode {
    return Object.hashAll([content]);
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

String _getSubsectionFromKey(String key) {
  switch (key) {
    case 'exp':
  }
}