RootView = require 'views/core/RootView'
CocoCollection = require 'collections/CocoCollection'
Classroom = require 'models/Classroom'
CourseInstance = require 'models/CourseInstance'
TrialRequest = require 'models/TrialRequest'
User = require 'models/User'
utils = require 'core/utils'

# TODO: match anonymous trial requests with real users via email
# TODO: sanitize and use student.schoolName, can't use it directly
# TODO: example untriaged student: no geo IP, not attached to teacher with school
# TODO: example untriaged teacher: deleted but owner of a classroom
# TODO: use student geoip on their teacher

module.exports = class SchoolCountsView extends RootView
  id: 'admin-school-counts-view'
  template: require 'templates/admin/school-counts'

  initialize: ->
    return super() unless me.isAdmin()
    @classrooms = new CocoCollection([], { url: "/db/classroom/-/users", model: Classroom })
    @supermodel.loadCollection(@classrooms, 'classrooms', {cache: false})
    @courseInstances = new CocoCollection([], { url: "/db/course_instance/-/non-hoc", model: CourseInstance})
    @supermodel.loadCollection(@courseInstances, 'course-instances', {cache: false})
    @students = new CocoCollection([], { url: "/db/user/-/students", model: User })
    @supermodel.loadCollection(@students, 'students', {cache: false})
    @teachers = new CocoCollection([], { url: "/db/user/-/teachers", model: User })
    @supermodel.loadCollection(@teachers, 'teachers', {cache: false})
    @trialRequests = new CocoCollection([], { url: "/db/trial.request/-/users", model: TrialRequest })
    @supermodel.loadCollection(@trialRequests, 'trial-requests', {cache: false})
    super()

  onLoaded: ->
    return super() unless me.isAdmin()

    console.log(new Date().toISOString(), 'onLoaded')

    teacherMap = {} # Used to make sure teachers and students only counted once
    studentMap = {} # Used to make sure teachers and students only counted once
    studentNonHocMap = {} # Used to exclude HoC users
    teacherStudentMap = {} # Used to link students to their teacher locations
    unknownSchoolCount = 1 # Used to separate unique but unknown schools

    console.log(new Date().toISOString(), "Processing #{@courseInstances.models.length} course instances...")
    for courseInstance in @courseInstances.models
      studentNonHocMap[courseInstance.get('ownerID')] = true
      studentNonHocMap[studentID] = true for studentID in courseInstance.get('members') ? []

    console.log(new Date().toISOString(), "Processing #{@teachers.models.length} teachers...")
    for teacher in @teachers.models
      teacherMap[teacher.id] = teacher.get('geo') ? {}

    console.log(new Date().toISOString(), "Processing #{@classrooms.models.length} classrooms...")
    for classroom in @classrooms.models
      teacherID = classroom.get('ownerID')
      teacherMap[teacherID] ?= {}
      teacherStudentMap[teacherID] ?= {}
      for studentID in classroom.get('members')
        continue if teacherMap[studentID]
        continue unless studentNonHocMap[studentID]
        studentMap[studentID] = {}
        teacherStudentMap[teacherID][studentID] = true

    console.log(new Date().toISOString(), "Processing #{@students.models.length} students...")
    for student in @students.models
      continue unless studentNonHocMap[student.id]
      continue if teacherMap[student.id]
      studentMap[student.id] = {geo: student.get('geo')}

    delete studentNonHocMap[studentId] for studentId in studentNonHocMap # Don't need these anymore

    console.log(new Date().toISOString(), "Cloning #{Object.keys(teacherMap).length} teacherMap...")
    orphanTeacherMap = {}
    orphanTeacherMap[teacherID] = true for teacherID of teacherMap
    console.log(new Date().toISOString(), "Cloning #{Object.keys(studentMap).length} studentMap...")
    orphanStudentMap = {}
    orphanStudentMap[studentID] = true for studentID of studentMap

    console.log(new Date().toISOString(), "Processing #{@trialRequests.models.length} trial requests...")
    countryStateDistrictSchoolCountsMap = {}
    for trialRequest in @trialRequests.models
      teacherID = trialRequest.get('applicant')
      unless teacherMap[teacherID]
        # E.g. parents
        # console.log("Skipping non-teacher #{teacherID} trial request #{trialRequest.id}")
        continue
      props = trialRequest.get('properties')
      if props.nces_id and props.country and props.state
        country = props.country
        state = props.state
        district = props.nces_district
        school = props.nces_name
        countryStateDistrictSchoolCountsMap[country] ?= {}
        countryStateDistrictSchoolCountsMap[country][state] ?= {}
        countryStateDistrictSchoolCountsMap[country][state][district] ?= {}
        countryStateDistrictSchoolCountsMap[country][state][district][school] ?= {students: {}, teachers: {}}
        countryStateDistrictSchoolCountsMap[country][state][district][school].teachers[teacherID] = true
        for studentID, val of teacherStudentMap[teacherID] when orphanStudentMap[studentID]
          countryStateDistrictSchoolCountsMap[country][state][district][school].students[studentID] = true
          delete orphanStudentMap[studentID]
        delete orphanTeacherMap[teacherID]
      else if not _.isEmpty(props.country)
        country = props.country?.trim()
        if _.isEmpty(country)
          country = 'unknown'
        else
          country = country[0].toUpperCase() + country.substring(1).toLowerCase()
          country = 'Taiwan' if /台灣/ig.test(country)
          country = 'UK' if /^uk$|united kingdom|england/ig.test(country)
          country = 'USA' if /^u\.s\.?(\.a)?\.?$|^us$|america|united states|usa/ig.test(country)
        state = props.state ? 'unknown'
        if country is 'USA'
          stateName = utils.usStateCodes.sanitizeStateName(state)
          state = utils.usStateCodes.getStateCodeByStateName(stateName) if stateName
          state = utils.usStateCodes.sanitizeStateCode(state) ? state
        district = 'unknown'
        school = props.organiziation ? 'unknown'
        countryStateDistrictSchoolCountsMap[country] ?= {}
        countryStateDistrictSchoolCountsMap[country][state] ?= {}
        countryStateDistrictSchoolCountsMap[country][state][district] ?= {}
        countryStateDistrictSchoolCountsMap[country][state][district][school] ?= {students: {}, teachers: {}}
        countryStateDistrictSchoolCountsMap[country][state][district][school].teachers[teacherID] = true
        for studentID, val of teacherStudentMap[teacherID] when orphanStudentMap[studentID]
          countryStateDistrictSchoolCountsMap[country][state][district][school].students[studentID] = true
          delete orphanStudentMap[studentID]
        delete orphanTeacherMap[teacherID]

    console.log(new Date().toISOString(), "Processing #{Object.keys(orphanTeacherMap).length} orphaned teachers with geo IPs...")
    for teacherID, val of orphanTeacherMap
      continue unless teacherMap[teacherID].country
      country = teacherMap[teacherID].countryName or teacherMap[teacherID].country
      country = 'UK' if country is 'GB' or country is 'United Kingdom'
      country = 'USA' if country is 'US' or country is 'United States'
      state = teacherMap[teacherID].region or 'unknown'
      district = 'unknown'
      school = 'unknown'
      if teacherStudentMap[teacherID] and Object.keys(teacherStudentMap[teacherID]).length >= 10
        school += unknownSchoolCount++
      countryStateDistrictSchoolCountsMap[country] ?= {}
      countryStateDistrictSchoolCountsMap[country][state] ?= {}
      countryStateDistrictSchoolCountsMap[country][state][district] ?= {}
      countryStateDistrictSchoolCountsMap[country][state][district][school] ?= {students: {}, teachers: {}}
      countryStateDistrictSchoolCountsMap[country][state][district][school].teachers[teacherID] = true
      if teacherStudentMap[teacherID] and Object.keys(teacherStudentMap[teacherID]).length >= 10
        for studentID, val of teacherStudentMap[teacherID] when orphanStudentMap[studentID]
          countryStateDistrictSchoolCountsMap[country][state][district][school].students[studentID] = true
          delete orphanStudentMap[studentID]
      delete orphanTeacherMap[teacherID]

    console.log(new Date().toISOString(), "Processing #{Object.keys(orphanTeacherMap).length} orphaned teachers with 10+ students...")
    for teacherID, val of orphanTeacherMap
      continue unless teacherStudentMap[teacherID] and Object.keys(teacherStudentMap[teacherID]).length >= 10
      country = 'unknown'
      state = 'unknown'
      district = 'unknown'
      school = "unknown#{unknownSchoolCount++}"
      countryStateDistrictSchoolCountsMap[country] ?= {}
      countryStateDistrictSchoolCountsMap[country][state] ?= {}
      countryStateDistrictSchoolCountsMap[country][state][district] ?= {}
      countryStateDistrictSchoolCountsMap[country][state][district][school] ?= {students: {}, teachers: {}}
      countryStateDistrictSchoolCountsMap[country][state][district][school].teachers[teacherID] = true
      for studentID, val of teacherStudentMap[teacherID] when orphanStudentMap[studentID]
        countryStateDistrictSchoolCountsMap[country][state][district][school].students[studentID] = true
        delete orphanStudentMap[studentID]
      delete orphanTeacherMap[teacherID]

    console.log(new Date().toISOString(), "Processing #{Object.keys(orphanStudentMap).length} orphaned students with geo IPs...")
    for studentID of orphanStudentMap
      continue unless studentMap[studentID].geo?.country
      country = studentMap[studentID].geo.countryName or studentMap[studentID].geo.country
      country = 'UK' if country is 'GB' or country is 'United Kingdom'
      country = 'USA' if country is 'US' or country is 'United States'
      state = studentMap[studentID].geo.region or 'unknown'
      district = 'unknown'
      school = 'unknown'
      countryStateDistrictSchoolCountsMap[country] ?= {}
      countryStateDistrictSchoolCountsMap[country][state] ?= {}
      countryStateDistrictSchoolCountsMap[country][state][district] ?= {}
      countryStateDistrictSchoolCountsMap[country][state][district][school] ?= {students: {}, teachers: {}}
      countryStateDistrictSchoolCountsMap[country][state][district][school].students[studentID] = true
      delete orphanStudentMap[studentID]

    console.log(new Date().toISOString(), 'Building country graphs...')
    @countryGraphs = {}
    @countryCounts = []
    totalStudents = 0
    totalTeachers = 0
    for country, stateDistrictSchoolCountsMap of countryStateDistrictSchoolCountsMap
      @countryGraphs[country] =
        districtCounts: []
        stateCounts: []
        stateCountsMap: {}
        totalSchools: 0
        totalStates: 0
        totalStudents: 0
        totalTeachers: 0
      for state, districtSchoolCountsMap of stateDistrictSchoolCountsMap
        if utils.usStateCodes.sanitizeStateCode(state)? or ['GU', 'PR'].indexOf(state) >= 0
          @countryGraphs[country].totalStates++
        stateData = {state: state, districts: 0, schools: 0, students: 0, teachers: 0}
        for district, schoolCountsMap of districtSchoolCountsMap
          stateData.districts++
          districtData = {state: state, district: district, schools: 0, students: 0, teachers: 0}
          for school, counts of schoolCountsMap
            studentCount = Object.keys(counts.students).length
            teacherCount = Object.keys(counts.teachers).length
            @countryGraphs[country].totalSchools++
            @countryGraphs[country].totalStudents += studentCount
            @countryGraphs[country].totalTeachers += teacherCount
            stateData.schools++
            stateData.students += studentCount
            stateData.teachers += teacherCount
            districtData.schools++
            districtData.students += studentCount
            districtData.teachers += teacherCount
          @countryGraphs[country].districtCounts.push(districtData)
        @countryGraphs[country].stateCounts.push(stateData)
        @countryGraphs[country].stateCountsMap[state] = stateData
      @countryCounts.push
        country: country
        schools: @countryGraphs[country].totalSchools
        students: @countryGraphs[country].totalStudents
        teachers: @countryGraphs[country].totalTeachers
      totalStudents += @countryGraphs[country].totalStudents
      totalTeachers += @countryGraphs[country].totalTeachers

    # Compare against orphanStudentMap and orphanTeacherMap to catch bugs
    @untriagedStudents = Object.keys(studentMap).length - totalStudents
    @untriagedTeachers = Object.keys(teacherMap).length - totalTeachers

    console.log(new Date().toISOString(), "teacherMap #{Object.keys(teacherMap).length} totalTeachers #{totalTeachers} orphanTeacherMap #{Object.keys(orphanTeacherMap).length}  @untriagedTeachers #{@untriagedTeachers}")
    console.log(new Date().toISOString(), "studentMap #{Object.keys(studentMap).length} totalStudents #{totalStudents} orphanStudentMap #{Object.keys(orphanStudentMap).length}  @untriagedStudents #{@untriagedStudents}")

    for country, graph of @countryGraphs
      graph.stateCounts.sort (a, b) ->
        b.students - a.students or b.teachers - a.teachers or b.schools - a.schools or b.districts - a.districts or b.state.localeCompare(a.state)
      graph.districtCounts.sort (a, b) ->
        if a.state isnt b.state
          stateCountsA = graph.stateCountsMap[a.state]
          stateCountsB = graph.stateCountsMap[b.state]
          stateCountsB.students - stateCountsA.students or stateCountsB.teachers - stateCountsA.teachers or stateCountsB.schools - stateCountsA.schools or stateCountsB.districts - stateCountsA.districts or a.state.localeCompare(b.state)
        else
          b.students - a.students or b.teachers - a.teachers or b.schools - a.schools or b.district.localeCompare(a.district)
    @countryCounts.sort (a, b) ->
      b.students - a.students or b.teachers - a.teachers or b.schools - a.schools or b.country.localeCompare(a.country)

    console.log(new Date().toISOString(), 'Done...')
    super()
